Merge branch 'integration_derpstances_groupview' into develop
This commit is contained in:
commit
1f6a484cb2
@ -186,7 +186,6 @@ configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/includ
|
|||||||
ADD_DEFINITIONS(-DQUAZIP_STATIC)
|
ADD_DEFINITIONS(-DQUAZIP_STATIC)
|
||||||
ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
|
ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
|
||||||
ADD_DEFINITIONS(-DLIBUTIL_STATIC)
|
ADD_DEFINITIONS(-DLIBUTIL_STATIC)
|
||||||
ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC)
|
|
||||||
|
|
||||||
######## Packaging/install paths setup ########
|
######## Packaging/install paths setup ########
|
||||||
|
|
||||||
@ -247,10 +246,6 @@ include_directories(${LIBUTIL_INCLUDE_DIR})
|
|||||||
add_subdirectory(depends/settings)
|
add_subdirectory(depends/settings)
|
||||||
include_directories(${LIBSETTINGS_INCLUDE_DIR})
|
include_directories(${LIBSETTINGS_INCLUDE_DIR})
|
||||||
|
|
||||||
# Add the group view library.
|
|
||||||
add_subdirectory(depends/groupview)
|
|
||||||
include_directories(${LIBGROUPVIEW_INCLUDE_DIR})
|
|
||||||
|
|
||||||
# Add the updater
|
# Add the updater
|
||||||
add_subdirectory(mmc_updater)
|
add_subdirectory(mmc_updater)
|
||||||
|
|
||||||
@ -317,8 +312,6 @@ gui/dialogs/UpdateDialog.cpp
|
|||||||
# GUI - widgets
|
# GUI - widgets
|
||||||
gui/widgets/Common.h
|
gui/widgets/Common.h
|
||||||
gui/widgets/Common.cpp
|
gui/widgets/Common.cpp
|
||||||
gui/widgets/InstanceDelegate.h
|
|
||||||
gui/widgets/InstanceDelegate.cpp
|
|
||||||
gui/widgets/ModListView.h
|
gui/widgets/ModListView.h
|
||||||
gui/widgets/ModListView.cpp
|
gui/widgets/ModListView.cpp
|
||||||
gui/widgets/VersionListView.h
|
gui/widgets/VersionListView.h
|
||||||
@ -328,6 +321,16 @@ gui/widgets/LabeledToolButton.cpp
|
|||||||
gui/widgets/MCModInfoFrame.h
|
gui/widgets/MCModInfoFrame.h
|
||||||
gui/widgets/MCModInfoFrame.cpp
|
gui/widgets/MCModInfoFrame.cpp
|
||||||
|
|
||||||
|
# GUI - instance group view
|
||||||
|
gui/groupview/Group.cpp
|
||||||
|
gui/groupview/Group.h
|
||||||
|
gui/groupview/GroupedProxyModel.cpp
|
||||||
|
gui/groupview/GroupedProxyModel.h
|
||||||
|
gui/groupview/GroupView.cpp
|
||||||
|
gui/groupview/GroupView.h
|
||||||
|
gui/groupview/InstanceDelegate.cpp
|
||||||
|
gui/groupview/InstanceDelegate.h
|
||||||
|
|
||||||
# Base classes and infrastructure
|
# Base classes and infrastructure
|
||||||
logic/BaseVersion.h
|
logic/BaseVersion.h
|
||||||
logic/MinecraftVersion.h
|
logic/MinecraftVersion.h
|
||||||
@ -411,10 +414,7 @@ logic/LegacyUpdate.cpp
|
|||||||
logic/LegacyForge.h
|
logic/LegacyForge.h
|
||||||
logic/LegacyForge.cpp
|
logic/LegacyForge.cpp
|
||||||
|
|
||||||
# 1.6 instances
|
# OneSix instances
|
||||||
logic/OneSixInstance.h
|
|
||||||
logic/OneSixInstance.cpp
|
|
||||||
logic/OneSixInstance_p.h
|
|
||||||
logic/OneSixUpdate.h
|
logic/OneSixUpdate.h
|
||||||
logic/OneSixUpdate.cpp
|
logic/OneSixUpdate.cpp
|
||||||
logic/OneSixVersion.h
|
logic/OneSixVersion.h
|
||||||
@ -425,10 +425,17 @@ logic/OneSixRule.h
|
|||||||
logic/OneSixRule.cpp
|
logic/OneSixRule.cpp
|
||||||
logic/OpSys.h
|
logic/OpSys.h
|
||||||
logic/OpSys.cpp
|
logic/OpSys.cpp
|
||||||
|
logic/BaseInstaller.h
|
||||||
|
logic/BaseInstaller.cpp
|
||||||
logic/ForgeInstaller.h
|
logic/ForgeInstaller.h
|
||||||
logic/ForgeInstaller.cpp
|
logic/ForgeInstaller.cpp
|
||||||
logic/LiteLoaderInstaller.h
|
logic/LiteLoaderInstaller.h
|
||||||
logic/LiteLoaderInstaller.cpp
|
logic/LiteLoaderInstaller.cpp
|
||||||
|
logic/OneSixInstance.h
|
||||||
|
logic/OneSixInstance.cpp
|
||||||
|
logic/OneSixInstance_p.h
|
||||||
|
logic/OneSixVersionBuilder.h
|
||||||
|
logic/OneSixVersionBuilder.cpp
|
||||||
|
|
||||||
# Nostalgia
|
# Nostalgia
|
||||||
logic/NostalgiaInstance.h
|
logic/NostalgiaInstance.h
|
||||||
@ -584,7 +591,7 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
|
|||||||
|
|
||||||
# Link
|
# Link
|
||||||
TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
|
TARGET_LINK_LIBRARIES(MultiMC MultiMC_common)
|
||||||
TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS})
|
TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings ${MultiMC_LINK_ADDITIONAL_LIBS})
|
||||||
QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||||
QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
project(libGroupView)
|
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
|
|
||||||
# Find Qt
|
|
||||||
find_package(Qt5Core REQUIRED)
|
|
||||||
find_package(Qt5Widgets REQUIRED)
|
|
||||||
|
|
||||||
# Include Qt headers.
|
|
||||||
include_directories(${Qt5Base_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
SET(LIBGROUPVIEW_HEADERS
|
|
||||||
include/groupview_config.h
|
|
||||||
|
|
||||||
# Public headers
|
|
||||||
include/categorizedsortfilterproxymodel.h
|
|
||||||
include/categorizedview.h
|
|
||||||
include/categorydrawer.h
|
|
||||||
|
|
||||||
# Private headers
|
|
||||||
src/categorizedsortfilterproxymodel_p.h
|
|
||||||
src/categorizedview_p.h
|
|
||||||
)
|
|
||||||
|
|
||||||
SET(LIBGROUPVIEW_SOURCES
|
|
||||||
src/categorizedsortfilterproxymodel.cpp
|
|
||||||
src/categorizedview.cpp
|
|
||||||
src/categorydrawer.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set the include dir path.
|
|
||||||
SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
|
||||||
|
|
||||||
# Include self.
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
||||||
include_directories(${CMAKE_BINARY_DIR}/include)
|
|
||||||
|
|
||||||
# Static link!
|
|
||||||
ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC)
|
|
||||||
|
|
||||||
add_definitions(-DLIBGROUPVIEW_LIBRARY)
|
|
||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
||||||
|
|
||||||
add_library(libGroupView STATIC ${LIBGROUPVIEW_SOURCES} ${LIBGROUPVIEW_HEADERS})
|
|
||||||
qt5_use_modules(libGroupView Core Widgets)
|
|
@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
* Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_H
|
|
||||||
#define KCATEGORIZEDSORTFILTERPROXYMODEL_H
|
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
#include <groupview_config.h>
|
|
||||||
|
|
||||||
class QItemSelection;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class lets you categorize a view. It is meant to be used along with
|
|
||||||
* KCategorizedView class.
|
|
||||||
*
|
|
||||||
* In general terms all you need to do is to reimplement subSortLessThan() and
|
|
||||||
* compareCategories() methods. In order to make categorization work, you need
|
|
||||||
* to also call setCategorizedModel() class to enable it, since the categorization
|
|
||||||
* is disabled by default.
|
|
||||||
*
|
|
||||||
* @see KCategorizedView
|
|
||||||
*
|
|
||||||
* @author Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
*/
|
|
||||||
class LIBGROUPVIEW_EXPORT KCategorizedSortFilterProxyModel
|
|
||||||
: public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum AdditionalRoles
|
|
||||||
{
|
|
||||||
// Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM))
|
|
||||||
// to define additional roles.
|
|
||||||
CategoryDisplayRole = 0x17CE990A, ///< This role is used for asking the category to a given index
|
|
||||||
|
|
||||||
CategorySortRole = 0x27857E60 ///< This role is used for sorting categories. You can return a
|
|
||||||
///< string or a long long value. Strings will be sorted alphabetically
|
|
||||||
///< while long long will be sorted by their value. Please note that this
|
|
||||||
///< value won't be shown on the view, is only for sorting purposes. What will
|
|
||||||
///< be shown as "Category" on the view will be asked with the role
|
|
||||||
///< CategoryDisplayRole.
|
|
||||||
};
|
|
||||||
|
|
||||||
KCategorizedSortFilterProxyModel ( QObject *parent = 0 );
|
|
||||||
virtual ~KCategorizedSortFilterProxyModel();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overridden from QSortFilterProxyModel. Sorts the source model using
|
|
||||||
* @p column for the given @p order.
|
|
||||||
*/
|
|
||||||
virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the model is categorized or not. Disabled by default.
|
|
||||||
*/
|
|
||||||
bool isCategorizedModel() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables the categorization feature.
|
|
||||||
*
|
|
||||||
* @param categorizedModel whether to enable or disable the categorization feature.
|
|
||||||
*/
|
|
||||||
void setCategorizedModel ( bool categorizedModel );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the column being used for sorting.
|
|
||||||
*/
|
|
||||||
int sortColumn() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the sort order being used for sorting.
|
|
||||||
*/
|
|
||||||
Qt::SortOrder sortOrder() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the sorting using CategorySortRole will use a natural comparison
|
|
||||||
* in the case that strings were returned. If enabled, QString::localeAwareCompare
|
|
||||||
* will be used for sorting.
|
|
||||||
*
|
|
||||||
* @param sortCategoriesByNaturalComparison whether to sort using a natural comparison or not.
|
|
||||||
*/
|
|
||||||
void setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether it is being used a natural comparison for sorting. Enabled by default.
|
|
||||||
*/
|
|
||||||
bool sortCategoriesByNaturalComparison() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Overridden from QSortFilterProxyModel. If you are subclassing
|
|
||||||
* KCategorizedSortFilterProxyModel, you will probably not need to reimplement this
|
|
||||||
* method.
|
|
||||||
*
|
|
||||||
* It calls compareCategories() to sort by category. If the both items are in the
|
|
||||||
* same category (i.e. compareCategories returns 0), then subSortLessThan is called.
|
|
||||||
*
|
|
||||||
* @return Returns true if the item @p left is less than the item @p right when sorting.
|
|
||||||
*
|
|
||||||
* @warning You usually won't need to reimplement this method when subclassing
|
|
||||||
* from KCategorizedSortFilterProxyModel.
|
|
||||||
*/
|
|
||||||
virtual bool lessThan ( const QModelIndex &left, const QModelIndex &right ) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method has a similar purpose as lessThan() has on QSortFilterProxyModel.
|
|
||||||
* It is used for sorting items that are in the same category.
|
|
||||||
*
|
|
||||||
* @return Returns true if the item @p left is less than the item @p right when sorting.
|
|
||||||
*/
|
|
||||||
virtual bool subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method compares the category of the @p left index with the category
|
|
||||||
* of the @p right index.
|
|
||||||
*
|
|
||||||
* Internally and if not reimplemented, this method will ask for @p left and
|
|
||||||
* @p right models for role CategorySortRole. In order to correctly sort
|
|
||||||
* categories, the data() metod of the model should return a qlonglong (or numeric) value, or
|
|
||||||
* a QString object. QString objects will be sorted with QString::localeAwareCompare if
|
|
||||||
* sortCategoriesByNaturalComparison() is true.
|
|
||||||
*
|
|
||||||
* @note Please have present that:
|
|
||||||
* QString(QChar(QChar::ObjectReplacementCharacter)) >
|
|
||||||
* QString(QChar(QChar::ReplacementCharacter)) >
|
|
||||||
* [ all possible strings ] >
|
|
||||||
* QString();
|
|
||||||
*
|
|
||||||
* This means that QString() will be sorted the first one, while
|
|
||||||
* QString(QChar(QChar::ObjectReplacementCharacter)) and
|
|
||||||
* QString(QChar(QChar::ReplacementCharacter)) will be sorted in last
|
|
||||||
* position.
|
|
||||||
*
|
|
||||||
* @warning Please note that data() method of the model should return always
|
|
||||||
* information of the same type. If you return a QString for an index,
|
|
||||||
* you should return always QStrings for all indexes for role CategorySortRole
|
|
||||||
* in order to correctly sort categories. You can't mix by returning
|
|
||||||
* a QString for one index, and a qlonglong for other.
|
|
||||||
*
|
|
||||||
* @note If you need a more complex layout, you will have to reimplement this
|
|
||||||
* method.
|
|
||||||
*
|
|
||||||
* @return A negative value if the category of @p left should be placed before the
|
|
||||||
* category of @p right. 0 if @p left and @p right are on the same category, and
|
|
||||||
* a positive value if the category of @p left should be placed after the
|
|
||||||
* category of @p right.
|
|
||||||
*/
|
|
||||||
virtual int compareCategories ( const QModelIndex &left, const QModelIndex &right ) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Private;
|
|
||||||
Private *const d;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // KCATEGORIZEDSORTFILTERPROXYMODEL_H
|
|
@ -1,332 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KCATEGORIZEDVIEW_H
|
|
||||||
#define KCATEGORIZEDVIEW_H
|
|
||||||
|
|
||||||
#include <QListView>
|
|
||||||
|
|
||||||
#include <groupview_config.h>
|
|
||||||
|
|
||||||
class KCategoryDrawer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @short Item view for listing items in a categorized fashion optionally
|
|
||||||
*
|
|
||||||
* KCategorizedView basically has the same functionality as QListView, only that it also lets you
|
|
||||||
* layout items in a way that they are categorized visually.
|
|
||||||
*
|
|
||||||
* For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer
|
|
||||||
* with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be
|
|
||||||
* flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true).
|
|
||||||
*
|
|
||||||
* The way it works (if categorization enabled):
|
|
||||||
*
|
|
||||||
* - When sorting, it does more things than QListView does. It will ask the model for the
|
|
||||||
* special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return
|
|
||||||
* a QString or an int in order to tell the view the order of categories. In this sense, for
|
|
||||||
* instance, if we are sorting by name ascending, "A" would be before than "B". If we are
|
|
||||||
* sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are
|
|
||||||
* also sorted.
|
|
||||||
*
|
|
||||||
* - When the view has to paint, it will ask the model with the role CategoryDisplayRole
|
|
||||||
* (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if
|
|
||||||
* we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example.
|
|
||||||
*
|
|
||||||
* For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own
|
|
||||||
* drawing.
|
|
||||||
*
|
|
||||||
* @note All examples cited before talk about filesystems and such, but have present that this
|
|
||||||
* is a completely generic class, and it can be used for whatever your purpose is. For
|
|
||||||
* instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In
|
|
||||||
* this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the
|
|
||||||
* same ("Mammal" and "Oviparous").
|
|
||||||
*
|
|
||||||
* @note There is a really performance boost if CategorySortRole returns an int instead of a QString.
|
|
||||||
* Have present that this role is asked (n * log n) times when sorting and compared. Comparing
|
|
||||||
* ints is always faster than comparing strings, whithout mattering how fast the string
|
|
||||||
* comparison is. Consider thinking of a way of returning ints instead of QStrings if your
|
|
||||||
* model can contain a high number of items.
|
|
||||||
*
|
|
||||||
* @warning Note that for really drawing items in blocks you will need some things to be done:
|
|
||||||
* - The model set to this view has to be (or inherit if you want to do special stuff
|
|
||||||
* in it) KCategorizedSortFilterProxyModel.
|
|
||||||
* - This model needs to be set setCategorizedModel to true.
|
|
||||||
* - Set a category drawer by calling setCategoryDrawer.
|
|
||||||
*
|
|
||||||
* @see KCategorizedSortFilterProxyModel, KCategoryDrawer
|
|
||||||
*
|
|
||||||
* @author Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
*/
|
|
||||||
class LIBGROUPVIEW_EXPORT KCategorizedView
|
|
||||||
: public QListView
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY ( int categorySpacing READ categorySpacing WRITE setCategorySpacing )
|
|
||||||
Q_PROPERTY ( bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors )
|
|
||||||
Q_PROPERTY ( bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks )
|
|
||||||
|
|
||||||
public:
|
|
||||||
KCategorizedView ( QWidget *parent = 0 );
|
|
||||||
|
|
||||||
~KCategorizedView();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void setModel ( QAbstractItemModel *model );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls to setGridSizeOwn().
|
|
||||||
*/
|
|
||||||
void setGridSize ( const QSize &size );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @warning note that setGridSize is not virtual in the base class (QListView), so if you are
|
|
||||||
* calling to this method, make sure you have a KCategorizedView pointer around. This
|
|
||||||
* means that something like:
|
|
||||||
* @code
|
|
||||||
* QListView *lv = new KCategorizedView();
|
|
||||||
* lv->setGridSize(mySize);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* will not call to the expected setGridSize method. Instead do something like this:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* QListView *lv;
|
|
||||||
* ...
|
|
||||||
* KCategorizedView *cv = qobject_cast<KCategorizedView*>(lv);
|
|
||||||
* if (cv) {
|
|
||||||
* cv->setGridSizeOwn(mySize);
|
|
||||||
* } else {
|
|
||||||
* lv->setGridSize(mySize);
|
|
||||||
* }
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* @note this method will call to QListView::setGridSize among other operations.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
void setGridSizeOwn ( const QSize &size );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual QRect visualRect ( const QModelIndex &index ) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current category drawer.
|
|
||||||
*/
|
|
||||||
KCategoryDrawer *categoryDrawer() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The category drawer that will be used for drawing categories.
|
|
||||||
*/
|
|
||||||
void setCategoryDrawer ( KCategoryDrawer *categoryDrawer );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Category spacing. The spacing between categories.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
int categorySpacing() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stablishes the category spacing. This is the spacing between categories.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
void setCategorySpacing ( int categorySpacing );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Whether blocks should be drawn with alternating colors.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
bool alternatingBlockColors() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether blocks should be drawn with alternating colors.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
void setAlternatingBlockColors ( bool enable );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Whether blocks can be collapsed or not.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
bool collapsibleBlocks() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether blocks can be collapsed or not.
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
void setCollapsibleBlocks ( bool enable );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Block of indexes that are into @p category.
|
|
||||||
*
|
|
||||||
* @since 4.5
|
|
||||||
*/
|
|
||||||
QModelIndexList block ( const QString &category );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Block of indexes that are represented by @p representative.
|
|
||||||
*
|
|
||||||
* @since 4.5
|
|
||||||
*/
|
|
||||||
QModelIndexList block ( const QModelIndex &representative );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual QModelIndex indexAt ( const QPoint &point ) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signify that all item delegates size hints return the same fixed size
|
|
||||||
*/
|
|
||||||
void setUniformItemWidths(bool enable);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do all item delegate size hints return the same fixed size?
|
|
||||||
*/
|
|
||||||
bool uniformItemWidths() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Reimplemented from QWidget.
|
|
||||||
*/
|
|
||||||
virtual void paintEvent ( QPaintEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QWidget.
|
|
||||||
*/
|
|
||||||
virtual void resizeEvent ( QResizeEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void setSelection ( const QRect &rect,
|
|
||||||
QItemSelectionModel::SelectionFlags flags );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QWidget.
|
|
||||||
*/
|
|
||||||
virtual void mouseMoveEvent ( QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QWidget.
|
|
||||||
*/
|
|
||||||
virtual void mousePressEvent ( QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QWidget.
|
|
||||||
*/
|
|
||||||
virtual void mouseReleaseEvent ( QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QWidget.
|
|
||||||
*/
|
|
||||||
virtual void leaveEvent ( QEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void startDrag ( Qt::DropActions supportedActions );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void dragMoveEvent ( QDragMoveEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void dragEnterEvent ( QDragEnterEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void dragLeaveEvent ( QDragLeaveEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void dropEvent ( QDropEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual QModelIndex moveCursor ( CursorAction cursorAction,
|
|
||||||
Qt::KeyboardModifiers modifiers );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void rowsAboutToBeRemoved ( const QModelIndex &parent,
|
|
||||||
int start,
|
|
||||||
int end );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void updateGeometries();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void currentChanged ( const QModelIndex ¤t,
|
|
||||||
const QModelIndex &previous );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void dataChanged ( const QModelIndex &topLeft,
|
|
||||||
const QModelIndex &bottomRight );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplemented from QAbstractItemView.
|
|
||||||
*/
|
|
||||||
virtual void rowsInserted ( const QModelIndex &parent,
|
|
||||||
int start,
|
|
||||||
int end );
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* Reposition items as needed.
|
|
||||||
*/
|
|
||||||
virtual void slotLayoutChanged();
|
|
||||||
virtual void slotCollapseOrExpandClicked ( QModelIndex );
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Private;
|
|
||||||
Private *const d;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // KCATEGORIZEDVIEW_H
|
|
@ -1,179 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KCATEGORYDRAWER_H
|
|
||||||
#define KCATEGORYDRAWER_H
|
|
||||||
|
|
||||||
#include <groupview_config.h>
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtGui/QMouseEvent>
|
|
||||||
|
|
||||||
class QPainter;
|
|
||||||
class QModelIndex;
|
|
||||||
class QStyleOption;
|
|
||||||
class KCategorizedView;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 4.5
|
|
||||||
*/
|
|
||||||
class LIBGROUPVIEW_EXPORT KCategoryDrawer
|
|
||||||
: public QObject
|
|
||||||
{
|
|
||||||
friend class KCategorizedView;
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
KCategoryDrawer ( KCategorizedView *view );
|
|
||||||
virtual ~KCategoryDrawer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The view this category drawer is associated with.
|
|
||||||
*/
|
|
||||||
KCategorizedView *view() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method purpose is to draw a category represented by the given
|
|
||||||
* @param index with the given @param sortRole sorting role
|
|
||||||
*
|
|
||||||
* @note This method will be called one time per category, always with the
|
|
||||||
* first element in that category
|
|
||||||
*/
|
|
||||||
virtual void drawCategory ( const QModelIndex &index,
|
|
||||||
int sortRole,
|
|
||||||
const QStyleOption &option,
|
|
||||||
QPainter *painter ) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The category height for the category representated by index @p index with
|
|
||||||
* style options @p option.
|
|
||||||
*/
|
|
||||||
virtual int categoryHeight ( const QModelIndex &index, const QStyleOption &option ) const;
|
|
||||||
|
|
||||||
//TODO KDE5: make virtual as leftMargin
|
|
||||||
/**
|
|
||||||
* @note 0 by default
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
int leftMargin() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note call to this method on the KCategoryDrawer constructor to set the left margin
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
void setLeftMargin ( int leftMargin );
|
|
||||||
|
|
||||||
//TODO KDE5: make virtual as rightMargin
|
|
||||||
/**
|
|
||||||
* @note 0 by default
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
int rightMargin() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @note call to this method on the KCategoryDrawer constructor to set the right margin
|
|
||||||
*
|
|
||||||
* @since 4.4
|
|
||||||
*/
|
|
||||||
void setRightMargin ( int rightMargin );
|
|
||||||
|
|
||||||
KCategoryDrawer &operator= ( const KCategoryDrawer &cd );
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Method called when the mouse button has been pressed.
|
|
||||||
*
|
|
||||||
* @param index The representative index of the block of items.
|
|
||||||
* @param blockRect The rect occupied by the block of items.
|
|
||||||
* @param event The mouse event.
|
|
||||||
*
|
|
||||||
* @warning You explicitly have to determine whether the event has been accepted or not. You
|
|
||||||
* have to call event->accept() or event->ignore() at all possible case branches in
|
|
||||||
* your code.
|
|
||||||
*/
|
|
||||||
virtual void mouseButtonPressed ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method called when the mouse button has been released.
|
|
||||||
*
|
|
||||||
* @param index The representative index of the block of items.
|
|
||||||
* @param blockRect The rect occupied by the block of items.
|
|
||||||
* @param event The mouse event.
|
|
||||||
*
|
|
||||||
* @warning You explicitly have to determine whether the event has been accepted or not. You
|
|
||||||
* have to call event->accept() or event->ignore() at all possible case branches in
|
|
||||||
* your code.
|
|
||||||
*/
|
|
||||||
virtual void mouseButtonReleased ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method called when the mouse has been moved.
|
|
||||||
*
|
|
||||||
* @param index The representative index of the block of items.
|
|
||||||
* @param blockRect The rect occupied by the block of items.
|
|
||||||
* @param event The mouse event.
|
|
||||||
*/
|
|
||||||
virtual void mouseMoved ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method called when the mouse button has been double clicked.
|
|
||||||
*
|
|
||||||
* @param index The representative index of the block of items.
|
|
||||||
* @param blockRect The rect occupied by the block of items.
|
|
||||||
* @param event The mouse event.
|
|
||||||
*
|
|
||||||
* @warning You explicitly have to determine whether the event has been accepted or not. You
|
|
||||||
* have to call event->accept() or event->ignore() at all possible case branches in
|
|
||||||
* your code.
|
|
||||||
*/
|
|
||||||
virtual void mouseButtonDoubleClicked ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method called when the mouse button has left this block.
|
|
||||||
*
|
|
||||||
* @param index The representative index of the block of items.
|
|
||||||
* @param blockRect The rect occupied by the block of items.
|
|
||||||
*/
|
|
||||||
virtual void mouseLeft ( const QModelIndex &index, const QRect &blockRect );
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Private;
|
|
||||||
Private *const d;
|
|
||||||
Q_SIGNALS:
|
|
||||||
/**
|
|
||||||
* This signal becomes emitted when collapse or expand has been clicked.
|
|
||||||
*/
|
|
||||||
void collapseOrExpandClicked ( const QModelIndex &index );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit this signal on your subclass implementation to notify that something happened. Usually
|
|
||||||
* this will be triggered when you have received an event, and its position matched some "hot spot".
|
|
||||||
*
|
|
||||||
* You give this action the integer you want, and having connected this signal to your code,
|
|
||||||
* the connected slot can perform the needed changes (view, model, selection model, delegate...)
|
|
||||||
*/
|
|
||||||
void actionRequested ( int action, const QModelIndex &index );
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // KCATEGORYDRAWER_H
|
|
@ -1,168 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
* Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "categorizedsortfilterproxymodel.h"
|
|
||||||
#include "categorizedsortfilterproxymodel_p.h"
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include <QItemSelection>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QSize>
|
|
||||||
|
|
||||||
KCategorizedSortFilterProxyModel::KCategorizedSortFilterProxyModel ( QObject *parent )
|
|
||||||
: QSortFilterProxyModel ( parent )
|
|
||||||
, d ( new Private() )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
KCategorizedSortFilterProxyModel::~KCategorizedSortFilterProxyModel()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategorizedSortFilterProxyModel::sort ( int column, Qt::SortOrder order )
|
|
||||||
{
|
|
||||||
d->sortColumn = column;
|
|
||||||
d->sortOrder = order;
|
|
||||||
|
|
||||||
QSortFilterProxyModel::sort ( column, order );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KCategorizedSortFilterProxyModel::isCategorizedModel() const
|
|
||||||
{
|
|
||||||
return d->categorizedModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategorizedSortFilterProxyModel::setCategorizedModel ( bool categorizedModel )
|
|
||||||
{
|
|
||||||
if ( categorizedModel == d->categorizedModel )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->categorizedModel = categorizedModel;
|
|
||||||
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
int KCategorizedSortFilterProxyModel::sortColumn() const
|
|
||||||
{
|
|
||||||
return d->sortColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::SortOrder KCategorizedSortFilterProxyModel::sortOrder() const
|
|
||||||
{
|
|
||||||
return d->sortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategorizedSortFilterProxyModel::setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison )
|
|
||||||
{
|
|
||||||
if ( sortCategoriesByNaturalComparison == d->sortCategoriesByNaturalComparison )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->sortCategoriesByNaturalComparison = sortCategoriesByNaturalComparison;
|
|
||||||
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KCategorizedSortFilterProxyModel::sortCategoriesByNaturalComparison() const
|
|
||||||
{
|
|
||||||
return d->sortCategoriesByNaturalComparison;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KCategorizedSortFilterProxyModel::lessThan ( const QModelIndex &left, const QModelIndex &right ) const
|
|
||||||
{
|
|
||||||
if ( d->categorizedModel )
|
|
||||||
{
|
|
||||||
int compare = compareCategories ( left, right );
|
|
||||||
|
|
||||||
if ( compare > 0 ) // left is greater than right
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if ( compare < 0 ) // left is less than right
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subSortLessThan ( left, right );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KCategorizedSortFilterProxyModel::subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const
|
|
||||||
{
|
|
||||||
return QSortFilterProxyModel::lessThan ( left, right );
|
|
||||||
}
|
|
||||||
|
|
||||||
int KCategorizedSortFilterProxyModel::compareCategories ( const QModelIndex &left, const QModelIndex &right ) const
|
|
||||||
{
|
|
||||||
QVariant l = ( left.model() ? left.model()->data ( left, CategorySortRole ) : QVariant() );
|
|
||||||
QVariant r = ( right.model() ? right.model()->data ( right, CategorySortRole ) : QVariant() );
|
|
||||||
|
|
||||||
Q_ASSERT ( l.isValid() );
|
|
||||||
Q_ASSERT ( r.isValid() );
|
|
||||||
Q_ASSERT ( l.type() == r.type() );
|
|
||||||
|
|
||||||
if ( l.type() == QVariant::String )
|
|
||||||
{
|
|
||||||
QString lstr = l.toString();
|
|
||||||
QString rstr = r.toString();
|
|
||||||
|
|
||||||
/*
|
|
||||||
if ( d->sortCategoriesByNaturalComparison )
|
|
||||||
{
|
|
||||||
return KStringHandler::naturalCompare ( lstr, rstr );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*/
|
|
||||||
if ( lstr < rstr )
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( lstr > rstr )
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong lint = l.toLongLong();
|
|
||||||
qlonglong rint = r.toLongLong();
|
|
||||||
|
|
||||||
if ( lint < rint )
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( lint > rint )
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
* Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_P_H
|
|
||||||
#define KCATEGORIZEDSORTFILTERPROXYMODEL_P_H
|
|
||||||
|
|
||||||
class KCategorizedSortFilterProxyModel;
|
|
||||||
|
|
||||||
class KCategorizedSortFilterProxyModel::Private
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Private()
|
|
||||||
: sortColumn ( 0 )
|
|
||||||
, sortOrder ( Qt::AscendingOrder )
|
|
||||||
, categorizedModel ( false )
|
|
||||||
, sortCategoriesByNaturalComparison ( true )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~Private()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int sortColumn;
|
|
||||||
Qt::SortOrder sortOrder;
|
|
||||||
bool categorizedModel;
|
|
||||||
bool sortCategoriesByNaturalComparison;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,160 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef KCATEGORIZEDVIEW_P_H
|
|
||||||
#define KCATEGORIZEDVIEW_P_H
|
|
||||||
|
|
||||||
class KCategorizedSortFilterProxyModel;
|
|
||||||
class KCategoryDrawer;
|
|
||||||
class KCategoryDrawerV2;
|
|
||||||
class KCategoryDrawerV3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
class KCategorizedView::Private
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct Block;
|
|
||||||
struct Item;
|
|
||||||
|
|
||||||
Private(KCategorizedView *q);
|
|
||||||
~Private();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether this view has all required elements to be categorized.
|
|
||||||
*/
|
|
||||||
bool isCategorized() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the block rect for the representative @p representative.
|
|
||||||
*/
|
|
||||||
QStyleOptionViewItemV4 blockRect(const QModelIndex &representative);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first and last element that intersects with rect.
|
|
||||||
*
|
|
||||||
* @note see that here we cannot take out items between first and last (as we could
|
|
||||||
* do with the rubberband).
|
|
||||||
*
|
|
||||||
* Complexity: O(log(n)) where n is model()->rowCount().
|
|
||||||
*/
|
|
||||||
QPair<QModelIndex, QModelIndex> intersectingIndexesWithRect(const QRect &rect) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the position of the block of @p category.
|
|
||||||
*
|
|
||||||
* Complexity: O(n) where n is the number of different categories when the block has been
|
|
||||||
* marked as in quarantine. O(1) the rest of the times (the vast majority).
|
|
||||||
*/
|
|
||||||
QPoint blockPosition(const QString &category);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the height of the block determined by @p category.
|
|
||||||
*/
|
|
||||||
int blockHeight(const QString &category);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the actual viewport width.
|
|
||||||
*/
|
|
||||||
int viewportWidth() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks all elements as in quarantine.
|
|
||||||
*
|
|
||||||
* Complexity: O(n) where n is model()->rowCount().
|
|
||||||
*
|
|
||||||
* @warning this is an expensive operation
|
|
||||||
*/
|
|
||||||
void regenerateAllElements();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update internal information, and keep sync with the real information that the model contains.
|
|
||||||
*/
|
|
||||||
void rowsInserted(const QModelIndex &parent, int start, int end);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns @p rect in viewport terms, taking in count horizontal and vertical offsets.
|
|
||||||
*/
|
|
||||||
QRect mapToViewport(const QRect &rect) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns @p rect in absolute terms, converted from viewport position.
|
|
||||||
*/
|
|
||||||
QRect mapFromViewport(const QRect &rect) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the height of the highest element in last row. This is only applicable if there is
|
|
||||||
* no grid set and uniformItemSizes is false.
|
|
||||||
*
|
|
||||||
* @param block in which block are we searching. Necessary to stop the search if we hit the
|
|
||||||
* first item in this block.
|
|
||||||
*/
|
|
||||||
int highestElementInLastRow(const Block &block) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the view has a valid grid size.
|
|
||||||
*/
|
|
||||||
bool hasGrid() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the category for the given index.
|
|
||||||
*/
|
|
||||||
QString categoryForIndex(const QModelIndex &index) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the visual rect for item when flow is LeftToRight.
|
|
||||||
*/
|
|
||||||
void leftToRightVisualRect(const QModelIndex &index, Item &item,
|
|
||||||
const Block &block, const QPoint &blockPos) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the visual rect for item when flow is TopToBottom.
|
|
||||||
* @note we only support viewMode == ListMode in this case.
|
|
||||||
*/
|
|
||||||
void topToBottomVisualRect(const QModelIndex &index, Item &item,
|
|
||||||
const Block &block, const QPoint &blockPos) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when expand or collapse has been clicked on the category drawer.
|
|
||||||
*/
|
|
||||||
void _k_slotCollapseOrExpandClicked(QModelIndex);
|
|
||||||
|
|
||||||
KCategorizedView *q = nullptr;
|
|
||||||
KCategorizedSortFilterProxyModel *proxyModel = nullptr;
|
|
||||||
KCategoryDrawer *categoryDrawer = nullptr;
|
|
||||||
int categorySpacing = 5;
|
|
||||||
bool alternatingBlockColors = false;
|
|
||||||
bool collapsibleBlocks = false;
|
|
||||||
bool constantItemWidth = false;
|
|
||||||
|
|
||||||
// FIXME: this is some really weird logic. Investigate.
|
|
||||||
Block *hoveredBlock;
|
|
||||||
QString hoveredCategory;
|
|
||||||
QModelIndex hoveredIndex;
|
|
||||||
|
|
||||||
QPoint pressedPosition;
|
|
||||||
QRect rubberBandRect;
|
|
||||||
|
|
||||||
QHash<QString, Block> blocks;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // KCATEGORIZEDVIEW_P_H
|
|
||||||
|
|
@ -1,231 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the KDE project
|
|
||||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Library General Public License
|
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "categorydrawer.h"
|
|
||||||
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QStyleOption>
|
|
||||||
#include <QApplication>
|
|
||||||
|
|
||||||
#include <categorizedview.h>
|
|
||||||
#include <categorizedsortfilterproxymodel.h>
|
|
||||||
|
|
||||||
#define HORIZONTAL_HINT 3
|
|
||||||
|
|
||||||
class KCategoryDrawer::Private
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Private(KCategorizedView *view)
|
|
||||||
: view(view)
|
|
||||||
, leftMargin(0)
|
|
||||||
, rightMargin(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~Private()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
KCategorizedView *view;
|
|
||||||
int leftMargin;
|
|
||||||
int rightMargin;
|
|
||||||
};
|
|
||||||
|
|
||||||
KCategoryDrawer::KCategoryDrawer(KCategorizedView *view)
|
|
||||||
: QObject(view)
|
|
||||||
, d(new Private(view))
|
|
||||||
{
|
|
||||||
setLeftMargin(2);
|
|
||||||
setRightMargin(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
KCategoryDrawer::~KCategoryDrawer()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void KCategoryDrawer::drawCategory(const QModelIndex &index,
|
|
||||||
int /*sortRole*/,
|
|
||||||
const QStyleOption &option,
|
|
||||||
QPainter *painter) const
|
|
||||||
{
|
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
|
|
||||||
const QRect optRect = option.rect;
|
|
||||||
QFont font(QApplication::font());
|
|
||||||
font.setBold(true);
|
|
||||||
const QFontMetrics fontMetrics = QFontMetrics(font);
|
|
||||||
|
|
||||||
QColor outlineColor = option.palette.text().color();
|
|
||||||
outlineColor.setAlphaF(0.35);
|
|
||||||
|
|
||||||
//BEGIN: top left corner
|
|
||||||
{
|
|
||||||
painter->save();
|
|
||||||
painter->setPen(outlineColor);
|
|
||||||
const QPointF topLeft(optRect.topLeft());
|
|
||||||
QRectF arc(topLeft, QSizeF(4, 4));
|
|
||||||
arc.translate(0.5, 0.5);
|
|
||||||
painter->drawArc(arc, 1440, 1440);
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
//END: top left corner
|
|
||||||
|
|
||||||
//BEGIN: left vertical line
|
|
||||||
{
|
|
||||||
QPoint start(optRect.topLeft());
|
|
||||||
start.ry() += 3;
|
|
||||||
QPoint verticalGradBottom(optRect.topLeft());
|
|
||||||
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
|
||||||
QLinearGradient gradient(start, verticalGradBottom);
|
|
||||||
gradient.setColorAt(0, outlineColor);
|
|
||||||
gradient.setColorAt(1, Qt::transparent);
|
|
||||||
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
|
||||||
}
|
|
||||||
//END: left vertical line
|
|
||||||
|
|
||||||
//BEGIN: horizontal line
|
|
||||||
{
|
|
||||||
QPoint start(optRect.topLeft());
|
|
||||||
start.rx() += 3;
|
|
||||||
QPoint horizontalGradTop(optRect.topLeft());
|
|
||||||
horizontalGradTop.rx() += optRect.width() - 6;
|
|
||||||
painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor);
|
|
||||||
}
|
|
||||||
//END: horizontal line
|
|
||||||
|
|
||||||
//BEGIN: top right corner
|
|
||||||
{
|
|
||||||
painter->save();
|
|
||||||
painter->setPen(outlineColor);
|
|
||||||
QPointF topRight(optRect.topRight());
|
|
||||||
topRight.rx() -= 4;
|
|
||||||
QRectF arc(topRight, QSizeF(4, 4));
|
|
||||||
arc.translate(0.5, 0.5);
|
|
||||||
painter->drawArc(arc, 0, 1440);
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
//END: top right corner
|
|
||||||
|
|
||||||
//BEGIN: right vertical line
|
|
||||||
{
|
|
||||||
QPoint start(optRect.topRight());
|
|
||||||
start.ry() += 3;
|
|
||||||
QPoint verticalGradBottom(optRect.topRight());
|
|
||||||
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
|
||||||
QLinearGradient gradient(start, verticalGradBottom);
|
|
||||||
gradient.setColorAt(0, outlineColor);
|
|
||||||
gradient.setColorAt(1, Qt::transparent);
|
|
||||||
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
|
||||||
}
|
|
||||||
//END: right vertical line
|
|
||||||
|
|
||||||
//BEGIN: text
|
|
||||||
{
|
|
||||||
QRect textRect(option.rect);
|
|
||||||
textRect.setTop(textRect.top() + 7);
|
|
||||||
textRect.setLeft(textRect.left() + 7);
|
|
||||||
textRect.setHeight(fontMetrics.height());
|
|
||||||
textRect.setRight(textRect.right() - 7);
|
|
||||||
|
|
||||||
painter->save();
|
|
||||||
painter->setFont(font);
|
|
||||||
QColor penColor(option.palette.text().color());
|
|
||||||
penColor.setAlphaF(0.6);
|
|
||||||
painter->setPen(penColor);
|
|
||||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category);
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
//END: text
|
|
||||||
}
|
|
||||||
|
|
||||||
int KCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &option) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(index);
|
|
||||||
Q_UNUSED(option)
|
|
||||||
|
|
||||||
QFont font(QApplication::font());
|
|
||||||
font.setBold(true);
|
|
||||||
QFontMetrics fontMetrics(font);
|
|
||||||
|
|
||||||
const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */
|
|
||||||
+ 11 /* top and bottom separation */;
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
int KCategoryDrawer::leftMargin() const
|
|
||||||
{
|
|
||||||
return d->leftMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::setLeftMargin(int leftMargin)
|
|
||||||
{
|
|
||||||
d->leftMargin = leftMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
int KCategoryDrawer::rightMargin() const
|
|
||||||
{
|
|
||||||
return d->rightMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::setRightMargin(int rightMargin)
|
|
||||||
{
|
|
||||||
d->rightMargin = rightMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
KCategoryDrawer &KCategoryDrawer::operator=(const KCategoryDrawer &cd)
|
|
||||||
{
|
|
||||||
d->leftMargin = cd.d->leftMargin;
|
|
||||||
d->rightMargin = cd.d->rightMargin;
|
|
||||||
d->view = cd.d->view;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
KCategorizedView *KCategoryDrawer::view() const
|
|
||||||
{
|
|
||||||
return d->view;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::mouseButtonPressed(const QModelIndex&, const QRect&, QMouseEvent *event)
|
|
||||||
{
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::mouseButtonReleased(const QModelIndex&, const QRect&, QMouseEvent *event)
|
|
||||||
{
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::mouseMoved(const QModelIndex&, const QRect&, QMouseEvent *event)
|
|
||||||
{
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::mouseButtonDoubleClicked(const QModelIndex&, const QRect&, QMouseEvent *event)
|
|
||||||
{
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KCategoryDrawer::mouseLeft(const QModelIndex&, const QRect&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "categorydrawer.moc"
|
|
@ -35,6 +35,9 @@ src/userutils.cpp
|
|||||||
|
|
||||||
include/cmdutils.h
|
include/cmdutils.h
|
||||||
src/cmdutils.cpp
|
src/cmdutils.cpp
|
||||||
|
|
||||||
|
include/modutils.h
|
||||||
|
src/modutils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the include dir path.
|
# Set the include dir path.
|
||||||
|
32
depends/util/include/modutils.h
Normal file
32
depends/util/include/modutils.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include "libutil_config.h"
|
||||||
|
|
||||||
|
class QUrl;
|
||||||
|
|
||||||
|
namespace Util
|
||||||
|
{
|
||||||
|
struct Version
|
||||||
|
{
|
||||||
|
Version(const QString &str);
|
||||||
|
|
||||||
|
bool operator<(const Version &other) const;
|
||||||
|
bool operator<=(const Version &other) const;
|
||||||
|
bool operator>(const Version &other) const;
|
||||||
|
bool operator==(const Version &other) const;
|
||||||
|
bool operator!=(const Version &other) const;
|
||||||
|
|
||||||
|
QString toString() const
|
||||||
|
{
|
||||||
|
return m_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
LIBUTIL_EXPORT QUrl expandQMURL(const QString &in);
|
||||||
|
LIBUTIL_EXPORT bool versionIsInInterval(const QString &version, const QString &interval);
|
||||||
|
}
|
||||||
|
|
216
depends/util/src/modutils.cpp
Normal file
216
depends/util/src/modutils.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#include "include/modutils.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
|
|
||||||
|
Util::Version::Version(const QString &str) : m_string(str)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Util::Version::operator<(const Version &other) const
|
||||||
|
{
|
||||||
|
QStringList parts1 = m_string.split('.');
|
||||||
|
QStringList parts2 = other.m_string.split('.');
|
||||||
|
|
||||||
|
while (!parts1.isEmpty() && !parts2.isEmpty())
|
||||||
|
{
|
||||||
|
QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst();
|
||||||
|
QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst();
|
||||||
|
bool ok1 = false;
|
||||||
|
bool ok2 = false;
|
||||||
|
int int1 = part1.toInt(&ok1);
|
||||||
|
int int2 = part2.toInt(&ok2);
|
||||||
|
if (ok1 && ok2)
|
||||||
|
{
|
||||||
|
if (int1 == int2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return int1 < int2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (part1 == part2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return part1 < part2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Util::Version::operator<=(const Util::Version &other) const
|
||||||
|
{
|
||||||
|
return *this < other || *this == other;
|
||||||
|
}
|
||||||
|
bool Util::Version::operator>(const Version &other) const
|
||||||
|
{
|
||||||
|
QStringList parts1 = m_string.split('.');
|
||||||
|
QStringList parts2 = other.m_string.split('.');
|
||||||
|
|
||||||
|
while (!parts1.isEmpty() && !parts2.isEmpty())
|
||||||
|
{
|
||||||
|
QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst();
|
||||||
|
QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst();
|
||||||
|
bool ok1 = false;
|
||||||
|
bool ok2 = false;
|
||||||
|
int int1 = part1.toInt(&ok1);
|
||||||
|
int int2 = part2.toInt(&ok2);
|
||||||
|
if (ok1 && ok2)
|
||||||
|
{
|
||||||
|
if (int1 == int2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return int1 > int2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (part1 == part2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return part1 > part2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool Util::Version::operator==(const Version &other) const
|
||||||
|
{
|
||||||
|
QStringList parts1 = m_string.split('.');
|
||||||
|
QStringList parts2 = other.m_string.split('.');
|
||||||
|
|
||||||
|
while (!parts1.isEmpty() && !parts2.isEmpty())
|
||||||
|
{
|
||||||
|
QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst();
|
||||||
|
QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst();
|
||||||
|
bool ok1 = false;
|
||||||
|
bool ok2 = false;
|
||||||
|
int int1 = part1.toInt(&ok1);
|
||||||
|
int int2 = part2.toInt(&ok2);
|
||||||
|
if (ok1 && ok2)
|
||||||
|
{
|
||||||
|
if (int1 == int2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (part1 == part2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool Util::Version::operator!=(const Version &other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl Util::expandQMURL(const QString &in)
|
||||||
|
{
|
||||||
|
QUrl inUrl(in);
|
||||||
|
if (inUrl.scheme() == "github")
|
||||||
|
{
|
||||||
|
// needed because QUrl makes the host all lower cases
|
||||||
|
const QString repo = in.mid(in.indexOf(inUrl.host(), 0, Qt::CaseInsensitive), inUrl.host().size());
|
||||||
|
QUrl out;
|
||||||
|
out.setScheme("https");
|
||||||
|
out.setHost("raw.github.com");
|
||||||
|
out.setPath(QString("/%1/%2/%3%4")
|
||||||
|
.arg(inUrl.userInfo(), repo,
|
||||||
|
inUrl.fragment().isEmpty() ? "master" : inUrl.fragment(), inUrl.path()));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
else if (inUrl.scheme() == "mcf")
|
||||||
|
{
|
||||||
|
QUrl out;
|
||||||
|
out.setScheme("http");
|
||||||
|
out.setHost("www.minecraftforum.net");
|
||||||
|
out.setPath(QString("/topic/%1-").arg(inUrl.path()));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Util::versionIsInInterval(const QString &version, const QString &interval)
|
||||||
|
{
|
||||||
|
if (interval.isEmpty() || version == interval)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interval notation is used
|
||||||
|
QRegularExpression exp(
|
||||||
|
"(?<start>[\\[\\]\\(\\)])(?<bottom>.*?)(,(?<top>.*?))?(?<end>[\\[\\]\\(\\)])");
|
||||||
|
QRegularExpressionMatch match = exp.match(interval);
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
const QChar start = match.captured("start").at(0);
|
||||||
|
const QChar end = match.captured("end").at(0);
|
||||||
|
const QString bottom = match.captured("bottom");
|
||||||
|
const QString top = match.captured("top");
|
||||||
|
|
||||||
|
// check if in range (bottom)
|
||||||
|
if (!bottom.isEmpty())
|
||||||
|
{
|
||||||
|
if ((start == '[') && !(version >= bottom))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((start == '(') && !(version > bottom))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if in range (top)
|
||||||
|
if (!top.isEmpty())
|
||||||
|
{
|
||||||
|
if ((end == ']') && !(version <= top))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((end == ')') && !(version < top))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@
|
|||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QKeyEvent>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@ -37,12 +38,12 @@
|
|||||||
#include "userutils.h"
|
#include "userutils.h"
|
||||||
#include "pathutils.h"
|
#include "pathutils.h"
|
||||||
|
|
||||||
#include "categorizedview.h"
|
#include "gui/groupview/GroupView.h"
|
||||||
#include "categorydrawer.h"
|
#include "gui/groupview/InstanceDelegate.h"
|
||||||
|
|
||||||
#include "gui/Platform.h"
|
#include "gui/Platform.h"
|
||||||
|
|
||||||
#include "gui/widgets/InstanceDelegate.h"
|
|
||||||
#include "gui/widgets/LabeledToolButton.h"
|
#include "gui/widgets/LabeledToolButton.h"
|
||||||
|
|
||||||
#include "gui/dialogs/SettingsDialog.h"
|
#include "gui/dialogs/SettingsDialog.h"
|
||||||
@ -140,21 +141,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
|
|
||||||
// Create the instance list widget
|
// Create the instance list widget
|
||||||
{
|
{
|
||||||
view = new KCategorizedView(ui->centralWidget);
|
view = new GroupView(ui->centralWidget);
|
||||||
drawer = new KCategoryDrawer(view);
|
|
||||||
|
|
||||||
view->setSelectionMode(QAbstractItemView::SingleSelection);
|
view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
view->setCategoryDrawer(drawer);
|
// view->setCategoryDrawer(drawer);
|
||||||
view->setCollapsibleBlocks(true);
|
// view->setCollapsibleBlocks(true);
|
||||||
view->setViewMode(QListView::IconMode);
|
// view->setViewMode(QListView::IconMode);
|
||||||
view->setFlow(QListView::LeftToRight);
|
// view->setFlow(QListView::LeftToRight);
|
||||||
view->setWordWrap(true);
|
// view->setWordWrap(true);
|
||||||
view->setMouseTracking(true);
|
// view->setMouseTracking(true);
|
||||||
view->viewport()->setAttribute(Qt::WA_Hover);
|
// view->viewport()->setAttribute(Qt::WA_Hover);
|
||||||
auto delegate = new ListViewDelegate();
|
auto delegate = new ListViewDelegate();
|
||||||
view->setItemDelegate(delegate);
|
view->setItemDelegate(delegate);
|
||||||
view->setSpacing(10);
|
// view->setSpacing(10);
|
||||||
view->setUniformItemWidths(true);
|
// view->setUniformItemWidths(true);
|
||||||
|
|
||||||
// do not show ugly blue border on the mac
|
// do not show ugly blue border on the mac
|
||||||
view->setAttribute(Qt::WA_MacShowFocusRect, false);
|
view->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||||
@ -331,7 +332,6 @@ MainWindow::~MainWindow()
|
|||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
delete proxymodel;
|
delete proxymodel;
|
||||||
delete drawer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::showInstanceContextMenu(const QPoint &pos)
|
void MainWindow::showInstanceContextMenu(const QPoint &pos)
|
||||||
|
@ -27,9 +27,6 @@
|
|||||||
class QToolButton;
|
class QToolButton;
|
||||||
class LabeledToolButton;
|
class LabeledToolButton;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class InstanceProxyModel;
|
|
||||||
class KCategorizedView;
|
|
||||||
class KCategoryDrawer;
|
|
||||||
class MinecraftProcess;
|
class MinecraftProcess;
|
||||||
class ConsoleWindow;
|
class ConsoleWindow;
|
||||||
|
|
||||||
@ -185,8 +182,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
KCategoryDrawer *drawer;
|
class GroupView *view;
|
||||||
KCategorizedView *view;
|
|
||||||
InstanceProxyModel *proxymodel;
|
InstanceProxyModel *proxymodel;
|
||||||
MinecraftProcess *proc;
|
MinecraftProcess *proc;
|
||||||
ConsoleWindow *console;
|
ConsoleWindow *console;
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "ui_IconPickerDialog.h"
|
#include "ui_IconPickerDialog.h"
|
||||||
|
|
||||||
#include "gui/Platform.h"
|
#include "gui/Platform.h"
|
||||||
#include "gui/widgets/InstanceDelegate.h"
|
#include "gui/groupview/InstanceDelegate.h"
|
||||||
|
|
||||||
#include "logic/icons/IconList.h"
|
#include "logic/icons/IconList.h"
|
||||||
|
|
||||||
|
@ -39,6 +39,18 @@
|
|||||||
#include "logic/lists/ForgeVersionList.h"
|
#include "logic/lists/ForgeVersionList.h"
|
||||||
#include "logic/ForgeInstaller.h"
|
#include "logic/ForgeInstaller.h"
|
||||||
#include "logic/LiteLoaderInstaller.h"
|
#include "logic/LiteLoaderInstaller.h"
|
||||||
|
#include "logic/OneSixVersionBuilder.h"
|
||||||
|
|
||||||
|
template<typename A, typename B>
|
||||||
|
QMap<A, B> invert(const QMap<B, A> &in)
|
||||||
|
{
|
||||||
|
QMap<A, B> out;
|
||||||
|
for (auto it = in.begin(); it != in.end(); ++it)
|
||||||
|
{
|
||||||
|
out.insert(it.value(), it.key());
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
||||||
: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
|
: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
|
||||||
@ -55,7 +67,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
|||||||
main_model->setSourceModel(m_version.get());
|
main_model->setSourceModel(m_version.get());
|
||||||
ui->libraryTreeView->setModel(main_model);
|
ui->libraryTreeView->setModel(main_model);
|
||||||
ui->libraryTreeView->installEventFilter(this);
|
ui->libraryTreeView->installEventFilter(this);
|
||||||
ui->mainClassEdit->setText(m_version->mainClass);
|
connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||||
|
this, &OneSixModEditDialog::versionCurrent);
|
||||||
updateVersionControls();
|
updateVersionControls();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -81,6 +94,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
|||||||
ui->resPackTreeView->installEventFilter(this);
|
ui->resPackTreeView->installEventFilter(this);
|
||||||
m_resourcepacks->startWatching();
|
m_resourcepacks->startWatching();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(m_inst, &OneSixInstance::versionReloaded, this, &OneSixModEditDialog::updateVersionControls);
|
||||||
}
|
}
|
||||||
|
|
||||||
OneSixModEditDialog::~OneSixModEditDialog()
|
OneSixModEditDialog::~OneSixModEditDialog()
|
||||||
@ -92,98 +107,138 @@ OneSixModEditDialog::~OneSixModEditDialog()
|
|||||||
|
|
||||||
void OneSixModEditDialog::updateVersionControls()
|
void OneSixModEditDialog::updateVersionControls()
|
||||||
{
|
{
|
||||||
bool customVersion = m_inst->versionIsCustom();
|
|
||||||
ui->customizeBtn->setEnabled(!customVersion);
|
|
||||||
ui->revertBtn->setEnabled(customVersion);
|
|
||||||
ui->forgeBtn->setEnabled(true);
|
ui->forgeBtn->setEnabled(true);
|
||||||
ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply());
|
ui->liteloaderBtn->setEnabled(LiteLoaderInstaller().canApply(m_inst));
|
||||||
ui->customEditorBtn->setEnabled(customVersion);
|
ui->mainClassEdit->setText(m_version->mainClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::disableVersionControls()
|
void OneSixModEditDialog::disableVersionControls()
|
||||||
{
|
{
|
||||||
ui->customizeBtn->setEnabled(false);
|
|
||||||
ui->revertBtn->setEnabled(false);
|
|
||||||
ui->forgeBtn->setEnabled(false);
|
ui->forgeBtn->setEnabled(false);
|
||||||
ui->liteloaderBtn->setEnabled(false);
|
ui->liteloaderBtn->setEnabled(false);
|
||||||
ui->customEditorBtn->setEnabled(false);
|
ui->reloadLibrariesBtn->setEnabled(false);
|
||||||
|
ui->removeLibraryBtn->setEnabled(false);
|
||||||
|
ui->mainClassEdit->setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_customizeBtn_clicked()
|
void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
|
||||||
{
|
{
|
||||||
if (m_inst->customizeVersion())
|
m_inst->reloadVersion(this);
|
||||||
{
|
|
||||||
m_version = m_inst->getFullVersion();
|
|
||||||
main_model->setSourceModel(m_version.get());
|
|
||||||
updateVersionControls();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_revertBtn_clicked()
|
void OneSixModEditDialog::on_removeLibraryBtn_clicked()
|
||||||
{
|
{
|
||||||
auto response = CustomMessageBox::selectable(
|
if (ui->libraryTreeView->currentIndex().isValid())
|
||||||
this, tr("Revert?"), tr("Do you want to revert the "
|
|
||||||
"version of this instance to its original configuration?"),
|
|
||||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec();
|
|
||||||
if (response == QMessageBox::Yes)
|
|
||||||
{
|
{
|
||||||
if (m_inst->revertCustomVersion())
|
if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
|
||||||
{
|
{
|
||||||
m_version = m_inst->getFullVersion();
|
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
|
||||||
main_model->setSourceModel(m_version.get());
|
}
|
||||||
updateVersionControls();
|
else
|
||||||
|
{
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_customEditorBtn_clicked()
|
void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
|
||||||
{
|
{
|
||||||
if (m_inst->versionIsCustom())
|
QDir(m_inst->instanceRoot()).remove("order.json");
|
||||||
{
|
m_inst->reloadVersion(this);
|
||||||
if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json"))
|
|
||||||
{
|
|
||||||
QMessageBox::warning(this, tr("Error"),
|
|
||||||
tr("Unable to open custom.json, check the settings"));
|
|
||||||
}
|
}
|
||||||
|
void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
|
||||||
|
{
|
||||||
|
|
||||||
|
QMap<QString, int> order = getExistingOrder();
|
||||||
|
if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row();
|
||||||
|
const QString ourId = m_version->versionFileId(ourRow);
|
||||||
|
const int ourOrder = order[ourId];
|
||||||
|
if (ourId.isNull() || ourId.startsWith("org.multimc."))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<int, QString> sortedOrder = invert(order);
|
||||||
|
|
||||||
|
QList<int> sortedOrders = sortedOrder.keys();
|
||||||
|
const int ourIndex = sortedOrders.indexOf(ourOrder);
|
||||||
|
if (ourIndex <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int ourNewOrder = sortedOrders.at(ourIndex - 1);
|
||||||
|
order[ourId] = ourNewOrder;
|
||||||
|
order[sortedOrder[sortedOrders[ourIndex - 1]]] = ourOrder;
|
||||||
|
|
||||||
|
if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
|
ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow - 1), QItemSelectionModel::SelectCurrent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
|
||||||
|
{
|
||||||
|
QMap<QString, int> order = getExistingOrder();
|
||||||
|
if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row();
|
||||||
|
const QString ourId = m_version->versionFileId(ourRow);
|
||||||
|
const int ourOrder = order[ourId];
|
||||||
|
if (ourId.isNull() || ourId.startsWith("org.multimc."))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<int, QString> sortedOrder = invert(order);
|
||||||
|
|
||||||
|
QList<int> sortedOrders = sortedOrder.keys();
|
||||||
|
const int ourIndex = sortedOrders.indexOf(ourOrder);
|
||||||
|
if ((ourIndex + 1) >= sortedOrders.size())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int ourNewOrder = sortedOrders.at(ourIndex + 1);
|
||||||
|
order[ourId] = ourNewOrder;
|
||||||
|
order[sortedOrder[sortedOrders[ourIndex + 1]]] = ourOrder;
|
||||||
|
|
||||||
|
if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
|
ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow + 1), QItemSelectionModel::SelectCurrent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_forgeBtn_clicked()
|
void OneSixModEditDialog::on_forgeBtn_clicked()
|
||||||
{
|
{
|
||||||
|
if (QDir(m_inst->instanceRoot()).exists("custom.json"))
|
||||||
|
{
|
||||||
|
if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDir(m_inst->instanceRoot()).remove("custom.json");
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
|
}
|
||||||
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
||||||
vselect.setFilter(1, m_inst->currentVersionId());
|
vselect.setFilter(1, m_inst->currentVersionId());
|
||||||
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
|
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
|
||||||
m_inst->currentVersionId());
|
m_inst->currentVersionId());
|
||||||
if (vselect.exec() && vselect.selectedVersion())
|
if (vselect.exec() && vselect.selectedVersion())
|
||||||
{
|
{
|
||||||
if (m_inst->versionIsCustom())
|
|
||||||
{
|
|
||||||
auto reply = QMessageBox::question(
|
|
||||||
this, tr("Revert?"),
|
|
||||||
tr("This will revert any "
|
|
||||||
"changes you did to the version up to this point. Is that "
|
|
||||||
"OK?"),
|
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
|
||||||
if (reply == QMessageBox::Yes)
|
|
||||||
{
|
|
||||||
m_inst->revertCustomVersion();
|
|
||||||
m_inst->customizeVersion();
|
|
||||||
{
|
|
||||||
m_version = m_inst->getFullVersion();
|
|
||||||
main_model->setSourceModel(m_version.get());
|
|
||||||
updateVersionControls();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_inst->customizeVersion();
|
|
||||||
m_version = m_inst->getFullVersion();
|
|
||||||
main_model->setSourceModel(m_version.get());
|
|
||||||
updateVersionControls();
|
|
||||||
}
|
|
||||||
ForgeVersionPtr forgeVersion =
|
ForgeVersionPtr forgeVersion =
|
||||||
std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
|
std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
|
||||||
if (!forgeVersion)
|
if (!forgeVersion)
|
||||||
@ -200,9 +255,9 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
|
|||||||
// install
|
// install
|
||||||
QString forgePath = entry->getFullPath();
|
QString forgePath = entry->getFullPath();
|
||||||
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||||
if (!forge.apply(m_version))
|
if (!forge.add(m_inst))
|
||||||
{
|
{
|
||||||
// failure notice
|
QLOG_ERROR() << "Failure installing forge";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -215,18 +270,28 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
|
|||||||
// install
|
// install
|
||||||
QString forgePath = entry->getFullPath();
|
QString forgePath = entry->getFullPath();
|
||||||
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||||
if (!forge.apply(m_version))
|
if (!forge.add(m_inst))
|
||||||
{
|
{
|
||||||
// failure notice
|
QLOG_ERROR() << "Failure installing forge";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
||||||
{
|
{
|
||||||
LiteLoaderInstaller liteloader(m_inst->intendedVersionId());
|
if (QDir(m_inst->instanceRoot()).exists("custom.json"))
|
||||||
if (!liteloader.canApply())
|
{
|
||||||
|
if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDir(m_inst->instanceRoot()).remove("custom.json");
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
|
}
|
||||||
|
LiteLoaderInstaller liteloader;
|
||||||
|
if (!liteloader.canApply(m_inst))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("LiteLoader"),
|
this, tr("LiteLoader"),
|
||||||
@ -234,19 +299,16 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
|||||||
"into this version of Minecraft"));
|
"into this version of Minecraft"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!m_inst->versionIsCustom())
|
if (!liteloader.add(m_inst))
|
||||||
{
|
|
||||||
m_inst->customizeVersion();
|
|
||||||
m_version = m_inst->getFullVersion();
|
|
||||||
main_model->setSourceModel(m_version.get());
|
|
||||||
updateVersionControls();
|
|
||||||
}
|
|
||||||
if (!liteloader.apply(m_version))
|
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("LiteLoader"),
|
QMessageBox::critical(this, tr("LiteLoader"),
|
||||||
tr("For reasons unknown, the LiteLoader installation failed. "
|
tr("For reasons unknown, the LiteLoader installation failed. "
|
||||||
"Check your MultiMC log files for details."));
|
"Check your MultiMC log files for details."));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_inst->reloadVersion(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
|
bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
|
||||||
@ -281,6 +343,35 @@ bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent)
|
|||||||
return QDialog::eventFilter(ui->resPackTreeView, keyEvent);
|
return QDialog::eventFilter(ui->resPackTreeView, keyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QMap<QString, int> OneSixModEditDialog::getExistingOrder() const
|
||||||
|
{
|
||||||
|
|
||||||
|
QMap<QString, int> order;
|
||||||
|
// default
|
||||||
|
{
|
||||||
|
for (OneSixVersion::VersionFile file : m_version->versionFiles)
|
||||||
|
{
|
||||||
|
if (file.id.startsWith("org.multimc."))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
order.insert(file.id, file.order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// overriden
|
||||||
|
{
|
||||||
|
QMap<QString, int> overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_inst);
|
||||||
|
for (auto id : order.keys())
|
||||||
|
{
|
||||||
|
if (overridenOrder.contains(id))
|
||||||
|
{
|
||||||
|
order[id] = overridenOrder[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev)
|
bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev)
|
||||||
{
|
{
|
||||||
if (ev->type() != QEvent::KeyPress)
|
if (ev->type() != QEvent::KeyPress)
|
||||||
@ -365,3 +456,15 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou
|
|||||||
Mod &m = m_mods->operator[](row);
|
Mod &m = m_mods->operator[](row);
|
||||||
ui->frame->updateWithMod(m);
|
ui->frame->updateWithMod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OneSixModEditDialog::versionCurrent(const QModelIndex ¤t, const QModelIndex &previous)
|
||||||
|
{
|
||||||
|
if (!current.isValid())
|
||||||
|
{
|
||||||
|
ui->removeLibraryBtn->setDisabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->removeLibraryBtn->setEnabled(m_version->canRemove(current.row()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -45,9 +45,11 @@ slots:
|
|||||||
void on_buttonBox_rejected();
|
void on_buttonBox_rejected();
|
||||||
void on_forgeBtn_clicked();
|
void on_forgeBtn_clicked();
|
||||||
void on_liteloaderBtn_clicked();
|
void on_liteloaderBtn_clicked();
|
||||||
void on_customizeBtn_clicked();
|
void on_reloadLibrariesBtn_clicked();
|
||||||
void on_revertBtn_clicked();
|
void on_removeLibraryBtn_clicked();
|
||||||
void on_customEditorBtn_clicked();
|
void on_resetLibraryOrderBtn_clicked();
|
||||||
|
void on_moveLibraryUpBtn_clicked();
|
||||||
|
void on_moveLibraryDownBtn_clicked();
|
||||||
void updateVersionControls();
|
void updateVersionControls();
|
||||||
void disableVersionControls();
|
void disableVersionControls();
|
||||||
|
|
||||||
@ -63,7 +65,11 @@ private:
|
|||||||
std::shared_ptr<ModList> m_resourcepacks;
|
std::shared_ptr<ModList> m_resourcepacks;
|
||||||
EnabledItemFilter *main_model;
|
EnabledItemFilter *main_model;
|
||||||
OneSixInstance *m_inst;
|
OneSixInstance *m_inst;
|
||||||
|
|
||||||
|
QMap<QString, int> getExistingOrder() const;
|
||||||
|
|
||||||
public
|
public
|
||||||
slots:
|
slots:
|
||||||
void loaderCurrent(QModelIndex current, QModelIndex previous);
|
void loaderCurrent(QModelIndex current, QModelIndex previous);
|
||||||
|
void versionCurrent(const QModelIndex ¤t, const QModelIndex &previous);
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>1</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="libTab">
|
<widget class="QWidget" name="libTab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
@ -43,6 +43,9 @@
|
|||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -84,62 +87,31 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="customizeBtn">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Create an customized copy of the base version</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Customize</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="revertBtn">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Revert to original base version</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Revert</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line">
|
<widget class="Line" name="line">
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Sunken</enum>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="addLibraryBtn">
|
<widget class="QPushButton" name="reloadLibrariesBtn">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Add new libraries</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Add</string>
|
<string>Reload</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="removeLibraryBtn">
|
<widget class="QPushButton" name="removeLibraryBtn">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Remove selected libraries</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Remove</string>
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="resetLibraryOrderBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Reset order</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -151,9 +123,16 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="customEditorBtn">
|
<widget class="QPushButton" name="moveLibraryUpBtn">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open custom.json</string>
|
<string>Move up</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="moveLibraryDownBtn">
|
||||||
|
<property name="text">
|
||||||
|
<string>Move down</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
269
gui/groupview/Group.cpp
Normal file
269
gui/groupview/Group.cpp
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
#include "Group.h"
|
||||||
|
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QtMath>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include "GroupView.h"
|
||||||
|
|
||||||
|
Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Group::Group(const Group *other)
|
||||||
|
: view(other->view), text(other->text), collapsed(other->collapsed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Group::update()
|
||||||
|
{
|
||||||
|
firstItemIndex = firstItem().row();
|
||||||
|
|
||||||
|
rowHeights = QVector<int>(numRows());
|
||||||
|
for (int i = 0; i < numRows(); ++i)
|
||||||
|
{
|
||||||
|
rowHeights[i] = view->categoryRowHeight(
|
||||||
|
view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Group::HitResults Group::hitScan(const QPoint &pos) const
|
||||||
|
{
|
||||||
|
Group::HitResults results = Group::NoHit;
|
||||||
|
int y_start = verticalPosition();
|
||||||
|
int body_start = y_start + headerHeight();
|
||||||
|
int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5?
|
||||||
|
int y = pos.y();
|
||||||
|
// int x = pos.x();
|
||||||
|
if (y < y_start)
|
||||||
|
{
|
||||||
|
results = Group::NoHit;
|
||||||
|
}
|
||||||
|
else if (y < body_start)
|
||||||
|
{
|
||||||
|
results = Group::HeaderHit;
|
||||||
|
int collapseSize = headerHeight() - 4;
|
||||||
|
|
||||||
|
// the icon
|
||||||
|
QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize);
|
||||||
|
if (iconRect.contains(pos))
|
||||||
|
{
|
||||||
|
results |= Group::CheckboxHit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (y < body_end)
|
||||||
|
{
|
||||||
|
results |= Group::BodyHit;
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option)
|
||||||
|
{
|
||||||
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
const QRect optRect = option.rect;
|
||||||
|
QFont font(QApplication::font());
|
||||||
|
font.setBold(true);
|
||||||
|
const QFontMetrics fontMetrics = QFontMetrics(font);
|
||||||
|
|
||||||
|
QColor outlineColor = option.palette.text().color();
|
||||||
|
outlineColor.setAlphaF(0.35);
|
||||||
|
|
||||||
|
//BEGIN: top left corner
|
||||||
|
{
|
||||||
|
painter->save();
|
||||||
|
painter->setPen(outlineColor);
|
||||||
|
const QPointF topLeft(optRect.topLeft());
|
||||||
|
QRectF arc(topLeft, QSizeF(4, 4));
|
||||||
|
arc.translate(0.5, 0.5);
|
||||||
|
painter->drawArc(arc, 1440, 1440);
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
//END: top left corner
|
||||||
|
|
||||||
|
//BEGIN: left vertical line
|
||||||
|
{
|
||||||
|
QPoint start(optRect.topLeft());
|
||||||
|
start.ry() += 3;
|
||||||
|
QPoint verticalGradBottom(optRect.topLeft());
|
||||||
|
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
||||||
|
QLinearGradient gradient(start, verticalGradBottom);
|
||||||
|
gradient.setColorAt(0, outlineColor);
|
||||||
|
gradient.setColorAt(1, Qt::transparent);
|
||||||
|
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
||||||
|
}
|
||||||
|
//END: left vertical line
|
||||||
|
|
||||||
|
//BEGIN: horizontal line
|
||||||
|
{
|
||||||
|
QPoint start(optRect.topLeft());
|
||||||
|
start.rx() += 3;
|
||||||
|
QPoint horizontalGradTop(optRect.topLeft());
|
||||||
|
horizontalGradTop.rx() += optRect.width() - 6;
|
||||||
|
painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor);
|
||||||
|
}
|
||||||
|
//END: horizontal line
|
||||||
|
|
||||||
|
//BEGIN: top right corner
|
||||||
|
{
|
||||||
|
painter->save();
|
||||||
|
painter->setPen(outlineColor);
|
||||||
|
QPointF topRight(optRect.topRight());
|
||||||
|
topRight.rx() -= 4;
|
||||||
|
QRectF arc(topRight, QSizeF(4, 4));
|
||||||
|
arc.translate(0.5, 0.5);
|
||||||
|
painter->drawArc(arc, 0, 1440);
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
//END: top right corner
|
||||||
|
|
||||||
|
//BEGIN: right vertical line
|
||||||
|
{
|
||||||
|
QPoint start(optRect.topRight());
|
||||||
|
start.ry() += 3;
|
||||||
|
QPoint verticalGradBottom(optRect.topRight());
|
||||||
|
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
||||||
|
QLinearGradient gradient(start, verticalGradBottom);
|
||||||
|
gradient.setColorAt(0, outlineColor);
|
||||||
|
gradient.setColorAt(1, Qt::transparent);
|
||||||
|
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
||||||
|
}
|
||||||
|
//END: right vertical line
|
||||||
|
|
||||||
|
//BEGIN: checkboxy thing
|
||||||
|
{
|
||||||
|
painter->save();
|
||||||
|
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||||
|
painter->setFont(font);
|
||||||
|
QColor penColor(option.palette.text().color());
|
||||||
|
penColor.setAlphaF(0.6);
|
||||||
|
painter->setPen(penColor);
|
||||||
|
QRect iconSubRect(option.rect);
|
||||||
|
iconSubRect.setTop(iconSubRect.top() + 7);
|
||||||
|
iconSubRect.setLeft(iconSubRect.left() + 7);
|
||||||
|
|
||||||
|
int sizing = fontMetrics.height();
|
||||||
|
int even = ( (sizing - 1) % 2 );
|
||||||
|
|
||||||
|
iconSubRect.setHeight(sizing - even);
|
||||||
|
iconSubRect.setWidth(sizing - even);
|
||||||
|
painter->drawRect(iconSubRect);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(collapsed)
|
||||||
|
painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "+");
|
||||||
|
else
|
||||||
|
painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "-");
|
||||||
|
*/
|
||||||
|
painter->setBrush(option.palette.text());
|
||||||
|
painter->fillRect(iconSubRect.x(), iconSubRect.y() + iconSubRect.height() / 2,
|
||||||
|
iconSubRect.width(), 2, penColor);
|
||||||
|
if (collapsed)
|
||||||
|
{
|
||||||
|
painter->fillRect(iconSubRect.x() + iconSubRect.width() / 2, iconSubRect.y(), 2,
|
||||||
|
iconSubRect.height(), penColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
//END: checkboxy thing
|
||||||
|
|
||||||
|
//BEGIN: text
|
||||||
|
{
|
||||||
|
QRect textRect(option.rect);
|
||||||
|
textRect.setTop(textRect.top() + 7);
|
||||||
|
textRect.setLeft(textRect.left() + 7 + fontMetrics.height() + 7);
|
||||||
|
textRect.setHeight(fontMetrics.height());
|
||||||
|
textRect.setRight(textRect.right() - 7);
|
||||||
|
|
||||||
|
painter->save();
|
||||||
|
painter->setFont(font);
|
||||||
|
QColor penColor(option.palette.text().color());
|
||||||
|
penColor.setAlphaF(0.6);
|
||||||
|
painter->setPen(penColor);
|
||||||
|
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
//END: text
|
||||||
|
}
|
||||||
|
|
||||||
|
int Group::totalHeight() const
|
||||||
|
{
|
||||||
|
return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'?
|
||||||
|
}
|
||||||
|
|
||||||
|
int Group::headerHeight() const
|
||||||
|
{
|
||||||
|
QFont font(QApplication::font());
|
||||||
|
font.setBold(true);
|
||||||
|
QFontMetrics fontMetrics(font);
|
||||||
|
|
||||||
|
const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */
|
||||||
|
+ 11 /* top and bottom separation */;
|
||||||
|
return height;
|
||||||
|
/*
|
||||||
|
int raw = view->viewport()->fontMetrics().height() + 4;
|
||||||
|
// add english. maybe. depends on font height.
|
||||||
|
if (raw % 2 == 0)
|
||||||
|
raw++;
|
||||||
|
return std::min(raw, 25);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
int Group::contentHeight() const
|
||||||
|
{
|
||||||
|
if (collapsed)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < rowHeights.size(); ++i)
|
||||||
|
{
|
||||||
|
result += rowHeights[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Group::numRows() const
|
||||||
|
{
|
||||||
|
return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Group::verticalPosition() const
|
||||||
|
{
|
||||||
|
return m_verticalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QModelIndex> Group::items() const
|
||||||
|
{
|
||||||
|
QList<QModelIndex> indices;
|
||||||
|
for (int i = 0; i < view->model()->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
const QModelIndex index = view->model()->index(i, 0);
|
||||||
|
if (index.data(GroupViewRoles::GroupRole).toString() == text)
|
||||||
|
{
|
||||||
|
indices.append(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Group::numItems() const
|
||||||
|
{
|
||||||
|
return items().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex Group::firstItem() const
|
||||||
|
{
|
||||||
|
QList<QModelIndex> indices = items();
|
||||||
|
return indices.isEmpty() ? QModelIndex() : indices.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex Group::lastItem() const
|
||||||
|
{
|
||||||
|
QList<QModelIndex> indices = items();
|
||||||
|
return indices.isEmpty() ? QModelIndex() : indices.last();
|
||||||
|
}
|
69
gui/groupview/Group.h
Normal file
69
gui/groupview/Group.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
class GroupView;
|
||||||
|
class QPainter;
|
||||||
|
class QModelIndex;
|
||||||
|
|
||||||
|
struct Group
|
||||||
|
{
|
||||||
|
/* constructors */
|
||||||
|
Group(const QString &text, GroupView *view);
|
||||||
|
Group(const Group *other);
|
||||||
|
|
||||||
|
/* data */
|
||||||
|
GroupView *view = nullptr;
|
||||||
|
QString text;
|
||||||
|
bool collapsed = false;
|
||||||
|
QVector<int> rowHeights;
|
||||||
|
int firstItemIndex = 0;
|
||||||
|
int m_verticalPosition = 0;
|
||||||
|
|
||||||
|
/* logic */
|
||||||
|
/// do stuff. and things. TODO: redo.
|
||||||
|
void update();
|
||||||
|
|
||||||
|
/// draw the header at y-position.
|
||||||
|
void drawHeader(QPainter *painter, const QStyleOptionViewItem &option);
|
||||||
|
|
||||||
|
/// height of the group, in total. includes a small bit of padding.
|
||||||
|
int totalHeight() const;
|
||||||
|
|
||||||
|
/// height of the group header, in pixels
|
||||||
|
int headerHeight() const;
|
||||||
|
|
||||||
|
/// height of the group content, in pixels
|
||||||
|
int contentHeight() const;
|
||||||
|
|
||||||
|
/// the number of visual rows this group has
|
||||||
|
int numRows() const;
|
||||||
|
|
||||||
|
/// the height at which this group starts, in pixels
|
||||||
|
int verticalPosition() const;
|
||||||
|
|
||||||
|
enum HitResult
|
||||||
|
{
|
||||||
|
NoHit = 0x0,
|
||||||
|
TextHit = 0x1,
|
||||||
|
CheckboxHit = 0x2,
|
||||||
|
HeaderHit = 0x4,
|
||||||
|
BodyHit = 0x8
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(HitResults, HitResult)
|
||||||
|
|
||||||
|
/// shoot! BANG! what did we hit?
|
||||||
|
HitResults hitScan (const QPoint &pos) const;
|
||||||
|
|
||||||
|
/// super derpy thing.
|
||||||
|
QList<QModelIndex> items() const;
|
||||||
|
/// I don't even
|
||||||
|
int numItems() const;
|
||||||
|
QModelIndex firstItem() const;
|
||||||
|
QModelIndex lastItem() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults)
|
935
gui/groupview/GroupView.cpp
Normal file
935
gui/groupview/GroupView.cpp
Normal file
@ -0,0 +1,935 @@
|
|||||||
|
#include "GroupView.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QtMath>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QPersistentModelIndex>
|
||||||
|
#include <QDrag>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
#include "Group.h"
|
||||||
|
|
||||||
|
template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2)
|
||||||
|
{
|
||||||
|
for (auto &item : l1)
|
||||||
|
{
|
||||||
|
if (t2.contains(item))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupView::GroupView(QWidget *parent)
|
||||||
|
: QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5),
|
||||||
|
m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0)
|
||||||
|
{
|
||||||
|
// setViewMode(IconMode);
|
||||||
|
// setMovement(Snap);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
|
// setWordWrap(true);
|
||||||
|
// setDragDropMode(QListView::InternalMove);
|
||||||
|
setAcceptDrops(true);
|
||||||
|
m_spacing = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupView::~GroupView()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_groups);
|
||||||
|
m_groups.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
||||||
|
const QVector<int> &roles)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if (roles.contains(GroupViewRoles::GroupRole) || roles.contains(Qt::DisplayRole))
|
||||||
|
{
|
||||||
|
*/
|
||||||
|
updateGeometries();
|
||||||
|
/*
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
void GroupView::rowsInserted(const QModelIndex &parent, int start, int end)
|
||||||
|
{
|
||||||
|
updateGeometries();
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
|
||||||
|
{
|
||||||
|
updateGeometries();
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::updateGeometries()
|
||||||
|
{
|
||||||
|
int previousScroll = verticalScrollBar()->value();
|
||||||
|
|
||||||
|
QMap<QString, Group *> cats;
|
||||||
|
|
||||||
|
for (int i = 0; i < model()->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
const QString groupName =
|
||||||
|
model()->index(i, 0).data(GroupViewRoles::GroupRole).toString();
|
||||||
|
if (!cats.contains(groupName))
|
||||||
|
{
|
||||||
|
Group *old = this->category(groupName);
|
||||||
|
if (old)
|
||||||
|
{
|
||||||
|
cats.insert(groupName, new Group(old));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cats.insert(groupName, new Group(groupName, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (m_editedCategory)
|
||||||
|
{
|
||||||
|
m_editedCategory = cats[m_editedCategory->text];
|
||||||
|
}*/
|
||||||
|
|
||||||
|
qDeleteAll(m_groups);
|
||||||
|
m_groups = cats.values();
|
||||||
|
|
||||||
|
for (auto cat : m_groups)
|
||||||
|
{
|
||||||
|
cat->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_groups.isEmpty())
|
||||||
|
{
|
||||||
|
verticalScrollBar()->setRange(0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int totalHeight = 0;
|
||||||
|
// top margin
|
||||||
|
totalHeight += m_categoryMargin;
|
||||||
|
for (auto category : m_groups)
|
||||||
|
{
|
||||||
|
category->m_verticalPosition = totalHeight;
|
||||||
|
totalHeight += category->totalHeight() + m_categoryMargin;
|
||||||
|
}
|
||||||
|
auto category = m_groups.last();
|
||||||
|
int itemScroll = category->contentHeight() / category->numRows();
|
||||||
|
/*
|
||||||
|
// remove the last margin (we don't want it)
|
||||||
|
totalHeight -= m_categoryMargin;
|
||||||
|
// add a margin on top...
|
||||||
|
totalHeight += m_categoryMargin;
|
||||||
|
*/
|
||||||
|
totalHeight += m_bottomMargin;
|
||||||
|
verticalScrollBar()->setSingleStep ( itemScroll );
|
||||||
|
const int rowsPerPage = qMax ( viewport()->height() / itemScroll, 1 );
|
||||||
|
verticalScrollBar()->setPageStep ( rowsPerPage * itemScroll );
|
||||||
|
|
||||||
|
verticalScrollBar()->setRange(0, totalHeight - height());
|
||||||
|
}
|
||||||
|
|
||||||
|
verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum()));
|
||||||
|
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupView::isIndexHidden(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Group *cat = category(index);
|
||||||
|
if (cat)
|
||||||
|
{
|
||||||
|
return cat->collapsed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Group *GroupView::category(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
return category(index.data(GroupViewRoles::GroupRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Group *GroupView::category(const QString &cat) const
|
||||||
|
{
|
||||||
|
for (auto group : m_groups)
|
||||||
|
{
|
||||||
|
if (group->text == cat)
|
||||||
|
{
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Group *GroupView::categoryAt(const QPoint &pos) const
|
||||||
|
{
|
||||||
|
for (auto group : m_groups)
|
||||||
|
{
|
||||||
|
if(group->hitScan(pos) & Group::CheckboxHit)
|
||||||
|
{
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GroupView::itemsPerRow() const
|
||||||
|
{
|
||||||
|
return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing));
|
||||||
|
}
|
||||||
|
|
||||||
|
int GroupView::contentWidth() const
|
||||||
|
{
|
||||||
|
return width() - m_leftMargin - m_rightMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GroupView::itemWidth() const
|
||||||
|
{
|
||||||
|
return itemDelegate()
|
||||||
|
->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0))
|
||||||
|
.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
int GroupView::categoryRowHeight(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QModelIndexList indices;
|
||||||
|
int internalRow = categoryInternalPosition(index).second;
|
||||||
|
for (auto &i : category(index)->items())
|
||||||
|
{
|
||||||
|
if (categoryInternalPosition(i).second == internalRow)
|
||||||
|
{
|
||||||
|
indices.append(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int largestHeight = 0;
|
||||||
|
for (auto &i : indices)
|
||||||
|
{
|
||||||
|
largestHeight =
|
||||||
|
qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height());
|
||||||
|
}
|
||||||
|
return largestHeight + m_spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<int, int> GroupView::categoryInternalPosition(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QList<QModelIndex> indices = category(index)->items();
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
const int perRow = itemsPerRow();
|
||||||
|
for (int i = 0; i < indices.size(); ++i)
|
||||||
|
{
|
||||||
|
if (indices.at(i) == index)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++x;
|
||||||
|
if (x == perRow)
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qMakePair(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GroupView::categoryInternalRowTop(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
Group *cat = category(index);
|
||||||
|
int categoryInternalRow = categoryInternalPosition(index).second;
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < categoryInternalRow; ++i)
|
||||||
|
{
|
||||||
|
result += cat->rowHeights.at(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const
|
||||||
|
{
|
||||||
|
for (auto &i : category->items())
|
||||||
|
{
|
||||||
|
QPair<int, int> pos = categoryInternalPosition(i);
|
||||||
|
if (pos.second == internalRow)
|
||||||
|
{
|
||||||
|
return categoryRowHeight(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
// endCategoryEditor();
|
||||||
|
|
||||||
|
QPoint pos = event->pos() + offset();
|
||||||
|
QPersistentModelIndex index = indexAt(pos);
|
||||||
|
|
||||||
|
m_pressedIndex = index;
|
||||||
|
m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
|
||||||
|
QItemSelectionModel::SelectionFlags selection_flags = selectionCommand(index, event);
|
||||||
|
m_pressedPosition = pos;
|
||||||
|
|
||||||
|
m_pressedCategory = categoryAt(m_pressedPosition);
|
||||||
|
if (m_pressedCategory)
|
||||||
|
{
|
||||||
|
setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState);
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.isValid() && (index.flags() & Qt::ItemIsEnabled))
|
||||||
|
{
|
||||||
|
// we disable scrollTo for mouse press so the item doesn't change position
|
||||||
|
// when the user is interacting with it (ie. clicking on it)
|
||||||
|
bool autoScroll = hasAutoScroll();
|
||||||
|
setAutoScroll(false);
|
||||||
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
|
||||||
|
setAutoScroll(autoScroll);
|
||||||
|
QRect rect(m_pressedPosition, pos);
|
||||||
|
setSelection(rect, QItemSelectionModel::ClearAndSelect);
|
||||||
|
|
||||||
|
// signal handlers may change the model
|
||||||
|
emit pressed(index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Forces a finalize() even if mouse is pressed, but not on a item
|
||||||
|
selectionModel()->select(QModelIndex(), QItemSelectionModel::Select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QPoint topLeft;
|
||||||
|
QPoint pos = event->pos() + offset();
|
||||||
|
|
||||||
|
if (state() == ExpandingState || state() == CollapsingState)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state() == DraggingState)
|
||||||
|
{
|
||||||
|
topLeft = m_pressedPosition - offset();
|
||||||
|
if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance())
|
||||||
|
{
|
||||||
|
m_pressedIndex = QModelIndex();
|
||||||
|
startDrag(model()->supportedDragActions());
|
||||||
|
setState(NoState);
|
||||||
|
stopAutoScroll();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionMode() != SingleSelection)
|
||||||
|
{
|
||||||
|
topLeft = m_pressedPosition - offset();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
topLeft = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pressedIndex.isValid() && (state() != DragSelectingState) &&
|
||||||
|
(event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty())
|
||||||
|
{
|
||||||
|
setState(DraggingState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((event->buttons() & Qt::LeftButton) && selectionModel())
|
||||||
|
{
|
||||||
|
setState(DragSelectingState);
|
||||||
|
|
||||||
|
setSelection(QRect(pos, pos), QItemSelectionModel::ClearAndSelect);
|
||||||
|
QModelIndex index = indexAt(pos);
|
||||||
|
|
||||||
|
// set at the end because it might scroll the view
|
||||||
|
if (index.isValid() && (index != selectionModel()->currentIndex()) &&
|
||||||
|
(index.flags() & Qt::ItemIsEnabled))
|
||||||
|
{
|
||||||
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QPoint pos = event->pos() + offset();
|
||||||
|
QPersistentModelIndex index = indexAt(pos);
|
||||||
|
|
||||||
|
bool click = (index == m_pressedIndex && index.isValid()) ||
|
||||||
|
(m_pressedCategory && m_pressedCategory == categoryAt(pos));
|
||||||
|
|
||||||
|
if (click && m_pressedCategory)
|
||||||
|
{
|
||||||
|
if (state() == ExpandingState)
|
||||||
|
{
|
||||||
|
m_pressedCategory->collapsed = false;
|
||||||
|
updateGeometries();
|
||||||
|
viewport()->update();
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (state() == CollapsingState)
|
||||||
|
{
|
||||||
|
m_pressedCategory->collapsed = true;
|
||||||
|
updateGeometries();
|
||||||
|
viewport()->update();
|
||||||
|
event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate;
|
||||||
|
|
||||||
|
setState(NoState);
|
||||||
|
|
||||||
|
if (click)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton)
|
||||||
|
{
|
||||||
|
emit clicked(index);
|
||||||
|
}
|
||||||
|
QStyleOptionViewItem option = viewOptions();
|
||||||
|
if (m_pressedAlreadySelected)
|
||||||
|
{
|
||||||
|
option.state |= QStyle::State_Selected;
|
||||||
|
}
|
||||||
|
if ((model()->flags(index) & Qt::ItemIsEnabled) &&
|
||||||
|
style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
|
||||||
|
{
|
||||||
|
emit activated(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QModelIndex index = indexAt(event->pos());
|
||||||
|
if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index))
|
||||||
|
{
|
||||||
|
QMouseEvent me(QEvent::MouseButtonPress, event->localPos(), event->windowPos(),
|
||||||
|
event->screenPos(), event->button(), event->buttons(),
|
||||||
|
event->modifiers());
|
||||||
|
mousePressEvent(&me);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// signal handlers may change the model
|
||||||
|
QPersistentModelIndex persistent = index;
|
||||||
|
emit doubleClicked(persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
QPainter painter(this->viewport());
|
||||||
|
|
||||||
|
QStyleOptionViewItemV4 option(viewOptions());
|
||||||
|
option.widget = this;
|
||||||
|
|
||||||
|
int wpWidth = viewport()->width();
|
||||||
|
option.rect.setWidth(wpWidth);
|
||||||
|
for (int i = 0; i < m_groups.size(); ++i)
|
||||||
|
{
|
||||||
|
Group *category = m_groups.at(i);
|
||||||
|
int y = category->verticalPosition();
|
||||||
|
y -= verticalOffset();
|
||||||
|
QRect backup = option.rect;
|
||||||
|
int height = category->totalHeight();
|
||||||
|
option.rect.setTop(y);
|
||||||
|
option.rect.setHeight(height);
|
||||||
|
option.rect.setLeft(m_leftMargin);
|
||||||
|
option.rect.setRight(wpWidth - m_rightMargin);
|
||||||
|
category->drawHeader(&painter, option);
|
||||||
|
y += category->totalHeight() + m_categoryMargin;
|
||||||
|
option.rect = backup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < model()->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
const QModelIndex index = model()->index(i, 0);
|
||||||
|
if (isIndexHidden(index))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Qt::ItemFlags flags = index.flags();
|
||||||
|
option.rect = visualRect(index);
|
||||||
|
option.features |=
|
||||||
|
QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway?
|
||||||
|
if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index))
|
||||||
|
{
|
||||||
|
option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected
|
||||||
|
: QStyle::State_None;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
option.state &= ~QStyle::State_Selected;
|
||||||
|
}
|
||||||
|
option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None;
|
||||||
|
if (!(flags & Qt::ItemIsEnabled))
|
||||||
|
{
|
||||||
|
option.state &= ~QStyle::State_Enabled;
|
||||||
|
}
|
||||||
|
itemDelegate()->paint(&painter, option, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drop indicators for manual reordering...
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
if (!m_lastDragPosition.isNull())
|
||||||
|
{
|
||||||
|
QPair<Group *, int> pair = rowDropPos(m_lastDragPosition);
|
||||||
|
Group *category = pair.first;
|
||||||
|
int row = pair.second;
|
||||||
|
if (category)
|
||||||
|
{
|
||||||
|
int internalRow = row - category->firstItemIndex;
|
||||||
|
QLine line;
|
||||||
|
if (internalRow >= category->numItems())
|
||||||
|
{
|
||||||
|
QRect toTheRightOfRect = visualRect(category->lastItem());
|
||||||
|
line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QRect toTheLeftOfRect = visualRect(model()->index(row, 0));
|
||||||
|
line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft());
|
||||||
|
}
|
||||||
|
painter.save();
|
||||||
|
painter.setPen(QPen(Qt::black, 3));
|
||||||
|
painter.drawLine(line);
|
||||||
|
painter.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
// QListView::resizeEvent(event);
|
||||||
|
|
||||||
|
// if (m_categoryEditor)
|
||||||
|
// {
|
||||||
|
// m_categoryEditor->resize(qMax(contentWidth() / 2,
|
||||||
|
// m_editedCategory->textRect.width()),
|
||||||
|
// m_categoryEditor->height());
|
||||||
|
// }
|
||||||
|
|
||||||
|
updateGeometries();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::dragEnterEvent(QDragEnterEvent *event)
|
||||||
|
{
|
||||||
|
if (!isDragEventAccepted(event))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_lastDragPosition = event->pos() + offset();
|
||||||
|
viewport()->update();
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::dragMoveEvent(QDragMoveEvent *event)
|
||||||
|
{
|
||||||
|
if (!isDragEventAccepted(event))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_lastDragPosition = event->pos() + offset();
|
||||||
|
viewport()->update();
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::dragLeaveEvent(QDragLeaveEvent *event)
|
||||||
|
{
|
||||||
|
m_lastDragPosition = QPoint();
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::dropEvent(QDropEvent *event)
|
||||||
|
{
|
||||||
|
m_lastDragPosition = QPoint();
|
||||||
|
|
||||||
|
stopAutoScroll();
|
||||||
|
setState(NoState);
|
||||||
|
|
||||||
|
if (event->source() != this || !(event->possibleActions() & Qt::MoveAction))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<Group *, int> dropPos = rowDropPos(event->pos() + offset());
|
||||||
|
const Group *category = dropPos.first;
|
||||||
|
const int row = dropPos.second;
|
||||||
|
|
||||||
|
if (row == -1)
|
||||||
|
{
|
||||||
|
viewport()->update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString categoryText = category->text;
|
||||||
|
if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex()))
|
||||||
|
{
|
||||||
|
model()->setData(model()->index(row, 0), categoryText,
|
||||||
|
GroupViewRoles::GroupRole);
|
||||||
|
event->setDropAction(Qt::MoveAction);
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
updateGeometries();
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::startDrag(Qt::DropActions supportedActions)
|
||||||
|
{
|
||||||
|
QModelIndexList indexes = selectionModel()->selectedIndexes();
|
||||||
|
if (indexes.count() > 0)
|
||||||
|
{
|
||||||
|
QMimeData *data = model()->mimeData(indexes);
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QRect rect;
|
||||||
|
QPixmap pixmap = renderToPixmap(indexes, &rect);
|
||||||
|
//rect.translate(offset());
|
||||||
|
// rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
|
||||||
|
QDrag *drag = new QDrag(this);
|
||||||
|
drag->setPixmap(pixmap);
|
||||||
|
drag->setMimeData(data);
|
||||||
|
Qt::DropAction defaultDropAction = Qt::IgnoreAction;
|
||||||
|
if (this->defaultDropAction() != Qt::IgnoreAction &&
|
||||||
|
(supportedActions & this->defaultDropAction()))
|
||||||
|
{
|
||||||
|
defaultDropAction = this->defaultDropAction();
|
||||||
|
}
|
||||||
|
if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
|
||||||
|
{
|
||||||
|
const QItemSelection selection = selectionModel()->selection();
|
||||||
|
|
||||||
|
for (auto it = selection.constBegin(); it != selection.constEnd(); ++it)
|
||||||
|
{
|
||||||
|
QModelIndex parent = (*it).parent();
|
||||||
|
if ((*it).left() != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((*it).right() != (model()->columnCount(parent) - 1))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int count = (*it).bottom() - (*it).top() + 1;
|
||||||
|
model()->removeRows((*it).top(), count, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect GroupView::visualRect(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
return geometryRect(index).translated(-offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect GroupView::geometryRect(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || isIndexHidden(index) || index.column() > 0)
|
||||||
|
{
|
||||||
|
return QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Group *cat = category(index);
|
||||||
|
QPair<int, int> pos = categoryInternalPosition(index);
|
||||||
|
int x = pos.first;
|
||||||
|
// int y = pos.second;
|
||||||
|
|
||||||
|
QRect out;
|
||||||
|
out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index));
|
||||||
|
out.setLeft(m_spacing + x * (itemWidth() + m_spacing));
|
||||||
|
out.setSize(itemDelegate()->sizeHint(viewOptions(), index));
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void CategorizedView::startCategoryEditor(Category *category)
|
||||||
|
{
|
||||||
|
if (m_categoryEditor != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_editedCategory = category;
|
||||||
|
m_categoryEditor = new QLineEdit(m_editedCategory->text, this);
|
||||||
|
QRect rect = m_editedCategory->textRect;
|
||||||
|
rect.setWidth(qMax(contentWidth() / 2, rect.width()));
|
||||||
|
m_categoryEditor->setGeometry(rect);
|
||||||
|
m_categoryEditor->show();
|
||||||
|
m_categoryEditor->setFocus();
|
||||||
|
connect(m_categoryEditor, &QLineEdit::returnPressed, this,
|
||||||
|
&CategorizedView::endCategoryEditor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CategorizedView::endCategoryEditor()
|
||||||
|
{
|
||||||
|
if (m_categoryEditor == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_editedCategory->text = m_categoryEditor->text();
|
||||||
|
m_updatesDisabled = true;
|
||||||
|
foreach (const QModelIndex &index, itemsForCategory(m_editedCategory))
|
||||||
|
{
|
||||||
|
const_cast<QAbstractItemModel *>(index.model())->setData(index,
|
||||||
|
m_categoryEditor->text(), CategoryRole);
|
||||||
|
}
|
||||||
|
m_updatesDisabled = false;
|
||||||
|
delete m_categoryEditor;
|
||||||
|
m_categoryEditor = 0;
|
||||||
|
m_editedCategory = 0;
|
||||||
|
updateGeometries();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
QModelIndex GroupView::indexAt(const QPoint &point) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model()->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
QModelIndex index = model()->index(i, 0);
|
||||||
|
if (geometryRect(index).contains(point))
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroupView::setSelection(const QRect &rect,
|
||||||
|
const QItemSelectionModel::SelectionFlags commands)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < model()->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
QModelIndex index = model()->index(i, 0);
|
||||||
|
QRect itemRect = geometryRect(index);
|
||||||
|
if (itemRect.intersects(rect))
|
||||||
|
{
|
||||||
|
selectionModel()->select(index, commands);
|
||||||
|
update(itemRect.translated(-offset()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(r);
|
||||||
|
auto paintPairs = draggablePaintPairs(indices, r);
|
||||||
|
if (paintPairs.isEmpty())
|
||||||
|
{
|
||||||
|
return QPixmap();
|
||||||
|
}
|
||||||
|
QPixmap pixmap(r->size());
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
QStyleOptionViewItem option = viewOptions();
|
||||||
|
option.state |= QStyle::State_Selected;
|
||||||
|
for (int j = 0; j < paintPairs.count(); ++j)
|
||||||
|
{
|
||||||
|
option.rect = paintPairs.at(j).first.translated(-r->topLeft());
|
||||||
|
const QModelIndex ¤t = paintPairs.at(j).second;
|
||||||
|
itemDelegate()->paint(&painter, option, current);
|
||||||
|
}
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QPair<QRect, QModelIndex>> GroupView::draggablePaintPairs(const QModelIndexList &indices,
|
||||||
|
QRect *r) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(r);
|
||||||
|
QRect &rect = *r;
|
||||||
|
QList<QPair<QRect, QModelIndex>> ret;
|
||||||
|
for (int i = 0; i < indices.count(); ++i)
|
||||||
|
{
|
||||||
|
const QModelIndex &index = indices.at(i);
|
||||||
|
const QRect current = geometryRect(index);
|
||||||
|
ret += qMakePair(current, index);
|
||||||
|
rect |= current;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupView::isDragEventAccepted(QDropEvent *event)
|
||||||
|
{
|
||||||
|
if (event->source() != this)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!model()->canDropMimeData(event->mimeData(), event->dropAction(),
|
||||||
|
rowDropPos(event->pos()).second, 0, QModelIndex()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
|
||||||
|
{
|
||||||
|
// check that we aren't on a category header and calculate which category we're in
|
||||||
|
Group *category = 0;
|
||||||
|
{
|
||||||
|
int y = 0;
|
||||||
|
for (auto cat : m_groups)
|
||||||
|
{
|
||||||
|
if (pos.y() > y && pos.y() < (y + cat->headerHeight()))
|
||||||
|
{
|
||||||
|
return qMakePair(nullptr, -1);
|
||||||
|
}
|
||||||
|
y += cat->totalHeight() + m_categoryMargin;
|
||||||
|
if (pos.y() < y)
|
||||||
|
{
|
||||||
|
category = cat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (category == 0)
|
||||||
|
{
|
||||||
|
return qMakePair(nullptr, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QModelIndex> indices = category->items();
|
||||||
|
|
||||||
|
// calculate the internal column
|
||||||
|
int internalColumn = -1;
|
||||||
|
{
|
||||||
|
const int itemWidth = this->itemWidth();
|
||||||
|
if (pos.x() >= (itemWidth * itemsPerRow()))
|
||||||
|
{
|
||||||
|
internalColumn = itemsPerRow();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c)
|
||||||
|
{
|
||||||
|
if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2))
|
||||||
|
{
|
||||||
|
internalColumn = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (internalColumn == -1)
|
||||||
|
{
|
||||||
|
return qMakePair(nullptr, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the internal row
|
||||||
|
int internalRow = -1;
|
||||||
|
{
|
||||||
|
// FIXME rework the drag and drop code
|
||||||
|
const int top = category->verticalPosition();
|
||||||
|
for (int r = 0, h = top; r < category->numRows();
|
||||||
|
h += itemHeightForCategoryRow(category, r), ++r)
|
||||||
|
{
|
||||||
|
if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r)))
|
||||||
|
{
|
||||||
|
internalRow = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (internalRow == -1)
|
||||||
|
{
|
||||||
|
return qMakePair(nullptr, -1);
|
||||||
|
}
|
||||||
|
// this happens if we're in the margin between a one category and another
|
||||||
|
// categories header
|
||||||
|
if (internalRow > (indices.size() / itemsPerRow()))
|
||||||
|
{
|
||||||
|
return qMakePair(nullptr, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flaten the internalColumn/internalRow to one row
|
||||||
|
int categoryRow = internalRow * itemsPerRow() + internalColumn;
|
||||||
|
|
||||||
|
// this is used if we're past the last item
|
||||||
|
if (categoryRow >= indices.size())
|
||||||
|
{
|
||||||
|
return qMakePair(category, indices.last().row() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return qMakePair(category, indices.at(categoryRow).row());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint GroupView::offset() const
|
||||||
|
{
|
||||||
|
return QPoint(horizontalOffset(), verticalOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) const
|
||||||
|
{
|
||||||
|
QRegion region;
|
||||||
|
for (auto &range : selection)
|
||||||
|
{
|
||||||
|
int start_row = range.top();
|
||||||
|
int end_row = range.bottom();
|
||||||
|
for (int row = start_row; row <= end_row; ++row)
|
||||||
|
{
|
||||||
|
int start_column = range.left();
|
||||||
|
int end_column = range.right();
|
||||||
|
for (int column = start_column; column <= end_column; ++column)
|
||||||
|
{
|
||||||
|
QModelIndex index = model()->index(row, column, rootIndex());
|
||||||
|
region += visualRect(index); // OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction,
|
||||||
|
Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
auto current = currentIndex();
|
||||||
|
if(!current.isValid())
|
||||||
|
{
|
||||||
|
qDebug() << "model row: invalid";
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
qDebug() << "model row: " << current.row();
|
||||||
|
auto cat = category(current);
|
||||||
|
int i = m_groups.indexOf(cat);
|
||||||
|
if(i >= 0)
|
||||||
|
{
|
||||||
|
// this is a pile of something foul
|
||||||
|
auto real_group = m_groups[i];
|
||||||
|
int beginning_row = 0;
|
||||||
|
for(auto group: m_groups)
|
||||||
|
{
|
||||||
|
if(group == real_group)
|
||||||
|
break;
|
||||||
|
beginning_row += group->numRows();
|
||||||
|
}
|
||||||
|
qDebug() << "category: " << real_group->text;
|
||||||
|
QPair<int, int> pos = categoryInternalPosition(current);
|
||||||
|
int row = beginning_row + pos.second;
|
||||||
|
qDebug() << "row: " << row;
|
||||||
|
qDebug() << "column: " << pos.first;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
139
gui/groupview/GroupView.h
Normal file
139
gui/groupview/GroupView.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QListView>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
struct GroupViewRoles
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GroupRole = Qt::UserRole,
|
||||||
|
ProgressValueRole,
|
||||||
|
ProgressMaximumRole
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Group;
|
||||||
|
|
||||||
|
class GroupView : public QAbstractItemView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GroupView(QWidget *parent = 0);
|
||||||
|
~GroupView();
|
||||||
|
|
||||||
|
QRect geometryRect(const QModelIndex &index) const;
|
||||||
|
virtual QRect visualRect(const QModelIndex &index) const override;
|
||||||
|
QModelIndex indexAt(const QPoint &point) const;
|
||||||
|
void setSelection(const QRect &rect,
|
||||||
|
const QItemSelectionModel::SelectionFlags commands) override;
|
||||||
|
|
||||||
|
virtual int horizontalOffset() const override
|
||||||
|
{
|
||||||
|
return horizontalScrollBar()->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int verticalOffset() const override
|
||||||
|
{
|
||||||
|
return verticalScrollBar()->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void scrollContentsBy(int dx, int dy) override
|
||||||
|
{
|
||||||
|
scrollDirtyRegion(dx, dy);
|
||||||
|
viewport()->scroll(dx, dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO!
|
||||||
|
*/
|
||||||
|
virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual QModelIndex moveCursor(CursorAction cursorAction,
|
||||||
|
Qt::KeyboardModifiers modifiers) override;
|
||||||
|
|
||||||
|
virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override;
|
||||||
|
|
||||||
|
protected
|
||||||
|
slots:
|
||||||
|
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
||||||
|
const QVector<int> &roles) override;
|
||||||
|
virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
|
||||||
|
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override;
|
||||||
|
virtual void updateGeometries() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool isIndexHidden(const QModelIndex &index) const override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
|
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||||
|
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||||
|
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||||
|
void dropEvent(QDropEvent *event) override;
|
||||||
|
|
||||||
|
void startDrag(Qt::DropActions supportedActions) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct Group;
|
||||||
|
|
||||||
|
QList<Group *> m_groups;
|
||||||
|
|
||||||
|
int m_leftMargin;
|
||||||
|
int m_rightMargin;
|
||||||
|
int m_bottomMargin;
|
||||||
|
int m_categoryMargin;
|
||||||
|
|
||||||
|
// bool m_updatesDisabled;
|
||||||
|
|
||||||
|
Group *category(const QModelIndex &index) const;
|
||||||
|
Group *category(const QString &cat) const;
|
||||||
|
Group *categoryAt(const QPoint &pos) const;
|
||||||
|
|
||||||
|
int itemsPerRow() const;
|
||||||
|
int contentWidth() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int itemWidth() const;
|
||||||
|
int categoryRowHeight(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
/*QLineEdit *m_categoryEditor;
|
||||||
|
Category *m_editedCategory;
|
||||||
|
void startCategoryEditor(Category *category);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void endCategoryEditor();*/
|
||||||
|
|
||||||
|
private: /* variables */
|
||||||
|
QPoint m_pressedPosition;
|
||||||
|
QPersistentModelIndex m_pressedIndex;
|
||||||
|
bool m_pressedAlreadySelected;
|
||||||
|
Group *m_pressedCategory;
|
||||||
|
QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag;
|
||||||
|
QPoint m_lastDragPosition;
|
||||||
|
int m_spacing = 5;
|
||||||
|
|
||||||
|
private: /* methods */
|
||||||
|
QPair<int, int> categoryInternalPosition(const QModelIndex &index) const;
|
||||||
|
int categoryInternalRowTop(const QModelIndex &index) const;
|
||||||
|
int itemHeightForCategoryRow(const Group *category, const int internalRow) const;
|
||||||
|
|
||||||
|
QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const;
|
||||||
|
QList<QPair<QRect, QModelIndex>> draggablePaintPairs(const QModelIndexList &indices,
|
||||||
|
QRect *r) const;
|
||||||
|
|
||||||
|
bool isDragEventAccepted(QDropEvent *event);
|
||||||
|
|
||||||
|
QPair<Group *, int> rowDropPos(const QPoint &pos);
|
||||||
|
|
||||||
|
QPoint offset() const;
|
||||||
|
};
|
26
gui/groupview/GroupedProxyModel.cpp
Normal file
26
gui/groupview/GroupedProxyModel.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "GroupedProxyModel.h"
|
||||||
|
|
||||||
|
#include "GroupView.h"
|
||||||
|
|
||||||
|
GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||||
|
{
|
||||||
|
const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString();
|
||||||
|
const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString();
|
||||||
|
if (leftCategory == rightCategory)
|
||||||
|
{
|
||||||
|
return subSortLessThan(left, right);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return leftCategory < rightCategory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GroupedProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||||
|
{
|
||||||
|
return left.row() < right.row();
|
||||||
|
}
|
15
gui/groupview/GroupedProxyModel.h
Normal file
15
gui/groupview/GroupedProxyModel.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
class GroupedProxyModel : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GroupedProxyModel(QObject *parent = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||||
|
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const;
|
||||||
|
};
|
@ -19,7 +19,32 @@
|
|||||||
#include <QTextLayout>
|
#include <QTextLayout>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QtCore/qmath.h>
|
#include <QtCore/qmath.h>
|
||||||
#include "Common.h"
|
|
||||||
|
#include "GroupView.h"
|
||||||
|
|
||||||
|
// Origin: Qt
|
||||||
|
static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
|
||||||
|
qreal &widthUsed)
|
||||||
|
{
|
||||||
|
height = 0;
|
||||||
|
widthUsed = 0;
|
||||||
|
textLayout.beginLayout();
|
||||||
|
QString str = textLayout.text();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
QTextLine line = textLayout.createLine();
|
||||||
|
if (!line.isValid())
|
||||||
|
break;
|
||||||
|
if (line.textLength() == 0)
|
||||||
|
break;
|
||||||
|
line.setLineWidth(lineWidth);
|
||||||
|
line.setPosition(QPointF(0, height));
|
||||||
|
height += line.height();
|
||||||
|
widthUsed = qMax(widthUsed, line.naturalTextWidth());
|
||||||
|
}
|
||||||
|
textLayout.endLayout();
|
||||||
|
}
|
||||||
|
|
||||||
#define QFIXED_MAX (INT_MAX / 256)
|
#define QFIXED_MAX (INT_MAX / 256)
|
||||||
|
|
||||||
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
||||||
@ -62,6 +87,27 @@ void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, cons
|
|||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this can be made a lot prettier
|
||||||
|
void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option,
|
||||||
|
const int value, const int maximum)
|
||||||
|
{
|
||||||
|
if (maximum == 0 || value == maximum)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->save();
|
||||||
|
|
||||||
|
qreal percent = (qreal)value / (qreal)maximum;
|
||||||
|
QColor color = option.palette.color(QPalette::Dark);
|
||||||
|
color.setAlphaF(0.70f);
|
||||||
|
painter->setBrush(color);
|
||||||
|
painter->setPen(QPen(QBrush(), 0));
|
||||||
|
painter->drawPie(option.rect, 90 * 16, -percent * 360 * 16);
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
|
||||||
static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option)
|
static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option)
|
||||||
{
|
{
|
||||||
QStyle *style = option->widget ? option->widget->style() : QApplication::style();
|
QStyle *style = option->widget ? option->widget->style() : QApplication::style();
|
||||||
@ -127,6 +173,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
|
|||||||
opt2.palette.setCurrentColorGroup(cg);
|
opt2.palette.setCurrentColorGroup(cg);
|
||||||
|
|
||||||
// fill in background, if any
|
// fill in background, if any
|
||||||
|
|
||||||
if (opt.backgroundBrush.style() != Qt::NoBrush)
|
if (opt.backgroundBrush.style() != Qt::NoBrush)
|
||||||
{
|
{
|
||||||
QPointF oldBO = painter->brushOrigin();
|
QPointF oldBO = painter->brushOrigin();
|
||||||
@ -135,6 +182,9 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
|
|||||||
painter->setBrushOrigin(oldBO);
|
painter->setBrushOrigin(oldBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawSelectionRect(painter, opt2, textHighlightRect);
|
||||||
|
|
||||||
|
/*
|
||||||
if (opt.showDecorationSelected)
|
if (opt.showDecorationSelected)
|
||||||
{
|
{
|
||||||
drawSelectionRect(painter, opt2, opt.rect);
|
drawSelectionRect(painter, opt2, opt.rect);
|
||||||
@ -154,6 +204,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
|
|||||||
drawFocusRect(painter, opt2, textHighlightRect);
|
drawFocusRect(painter, opt2, textHighlightRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw the icon
|
// draw the icon
|
||||||
@ -206,6 +257,10 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
|
|||||||
line.draw(painter, position);
|
line.draw(painter, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawProgressOverlay(painter, opt,
|
||||||
|
index.data(GroupViewRoles::ProgressValueRole).toInt(),
|
||||||
|
index.data(GroupViewRoles::ProgressMaximumRole).toInt());
|
||||||
|
|
||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,9 @@ class ListViewDelegate : public QStyledItemDelegate
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ListViewDelegate(QObject *parent = 0);
|
explicit ListViewDelegate(QObject *parent = 0);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
|
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const;
|
||||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
};
|
};
|
66
logic/BaseInstaller.cpp
Normal file
66
logic/BaseInstaller.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* Copyright 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BaseInstaller.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "OneSixVersion.h"
|
||||||
|
#include "OneSixLibrary.h"
|
||||||
|
#include "OneSixInstance.h"
|
||||||
|
|
||||||
|
#include "cmdutils.h"
|
||||||
|
|
||||||
|
BaseInstaller::BaseInstaller()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstaller::isApplied(OneSixInstance *on)
|
||||||
|
{
|
||||||
|
return QFile::exists(filename(on->instanceRoot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstaller::add(OneSixInstance *to)
|
||||||
|
{
|
||||||
|
if (!patchesDir(to->instanceRoot()).exists())
|
||||||
|
{
|
||||||
|
QDir(to->instanceRoot()).mkdir("patches");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isApplied(to))
|
||||||
|
{
|
||||||
|
if (!remove(to))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstaller::remove(OneSixInstance *from)
|
||||||
|
{
|
||||||
|
return QFile::remove(filename(from->instanceRoot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstaller::filename(const QString &root) const
|
||||||
|
{
|
||||||
|
return patchesDir(root).absoluteFilePath(id() + ".json");
|
||||||
|
}
|
||||||
|
QDir BaseInstaller::patchesDir(const QString &root) const
|
||||||
|
{
|
||||||
|
return QDir(root + "/patches/");
|
||||||
|
}
|
@ -15,14 +15,25 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtCore/QtGlobal>
|
#include <memory>
|
||||||
|
|
||||||
#ifdef LIBGROUPVIEW_STATIC
|
class OneSixInstance;
|
||||||
#define LIBGROUPVIEW_EXPORT
|
class QDir;
|
||||||
#else
|
class QString;
|
||||||
#ifdef LIBGROUPVIEW_LIBRARY
|
|
||||||
#define LIBGROUPVIEW_EXPORT Q_DECL_EXPORT
|
class BaseInstaller
|
||||||
#else
|
{
|
||||||
#define LIBGROUPVIEW_EXPORT Q_DECL_IMPORT
|
public:
|
||||||
#endif
|
BaseInstaller();
|
||||||
#endif
|
|
||||||
|
virtual bool canApply(OneSixInstance *instance) const { return true; }
|
||||||
|
bool isApplied(OneSixInstance *on);
|
||||||
|
|
||||||
|
virtual bool add(OneSixInstance *to);
|
||||||
|
virtual bool remove(OneSixInstance *from);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QString id() const = 0;
|
||||||
|
QString filename(const QString &root) const;
|
||||||
|
QDir patchesDir(const QString &root) const;
|
||||||
|
};
|
@ -21,7 +21,15 @@
|
|||||||
#include <quazipfile.h>
|
#include <quazipfile.h>
|
||||||
#include <pathutils.h>
|
#include <pathutils.h>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
#include "MultiMC.h"
|
#include "MultiMC.h"
|
||||||
|
#include "OneSixInstance.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QSaveFile>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
|
||||||
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
|
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
|
||||||
{
|
{
|
||||||
@ -66,6 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
|
|||||||
QJsonObject installObj = installVal.toObject();
|
QJsonObject installObj = installVal.toObject();
|
||||||
QString libraryName = installObj.value("path").toString();
|
QString libraryName = installObj.value("path").toString();
|
||||||
internalPath = installObj.value("filePath").toString();
|
internalPath = installObj.value("filePath").toString();
|
||||||
|
m_forgeVersionString = installObj.value("version").toString().remove("Forge").trimmed();
|
||||||
|
|
||||||
// where do we put the library? decode the mojang path
|
// where do we put the library? decode the mojang path
|
||||||
OneSixLibrary lib(libraryName);
|
OneSixLibrary lib(libraryName);
|
||||||
@ -103,13 +112,22 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
|
|||||||
realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
|
realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
|
bool ForgeInstaller::add(OneSixInstance *to)
|
||||||
{
|
{
|
||||||
|
if (!BaseInstaller::add(to))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject obj;
|
||||||
|
obj.insert("order", 5);
|
||||||
|
|
||||||
if (!m_forge_version)
|
if (!m_forge_version)
|
||||||
return false;
|
return false;
|
||||||
to->externalUpdateStart();
|
|
||||||
int sliding_insert_window = 0;
|
int sliding_insert_window = 0;
|
||||||
{
|
{
|
||||||
|
QJsonArray librariesPlus;
|
||||||
|
|
||||||
// for each library in the version we are adding (except for the blacklisted)
|
// for each library in the version we are adding (except for the blacklisted)
|
||||||
QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
|
QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
|
||||||
for (auto lib : m_forge_version->libraries)
|
for (auto lib : m_forge_version->libraries)
|
||||||
@ -128,28 +146,83 @@ bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
|
|||||||
if (blacklist.contains(libName))
|
if (blacklist.contains(libName))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// find an entry that matches this one
|
QJsonObject libObj = lib->toJson();
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto tolib : to->libraries)
|
bool equals = false;
|
||||||
|
// find an entry that matches this one
|
||||||
|
for (auto tolib : to->getVanillaVersion()->libraries)
|
||||||
{
|
{
|
||||||
if (tolib->name() != libName)
|
if (tolib->name() != libName)
|
||||||
continue;
|
continue;
|
||||||
found = true;
|
found = true;
|
||||||
|
if (tolib->toJson() == libObj)
|
||||||
|
{
|
||||||
|
equals = true;
|
||||||
|
}
|
||||||
// replace lib
|
// replace lib
|
||||||
tolib = lib;
|
libObj.insert("insert", QString("replace"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (equals)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
// add lib
|
// add lib
|
||||||
to->libraries.insert(sliding_insert_window, lib);
|
libObj.insert("insert", QString("prepend"));
|
||||||
|
if (lib->name() == "minecraftforge")
|
||||||
|
{
|
||||||
|
libObj.insert("MMC-depend", QString("hard"));
|
||||||
|
}
|
||||||
sliding_insert_window++;
|
sliding_insert_window++;
|
||||||
}
|
}
|
||||||
|
librariesPlus.prepend(libObj);
|
||||||
}
|
}
|
||||||
to->mainClass = m_forge_version->mainClass;
|
obj.insert("+libraries", librariesPlus);
|
||||||
to->minecraftArguments = m_forge_version->minecraftArguments;
|
obj.insert("mainClass", m_forge_version->mainClass);
|
||||||
to->processArguments = m_forge_version->processArguments;
|
QString args = m_forge_version->minecraftArguments;
|
||||||
|
QStringList tweakers;
|
||||||
|
{
|
||||||
|
QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
|
||||||
|
QRegularExpressionMatch match = expression.match(args);
|
||||||
|
while (match.hasMatch())
|
||||||
|
{
|
||||||
|
tweakers.append(match.captured(1));
|
||||||
|
args.remove(match.capturedStart(), match.capturedLength());
|
||||||
|
match = expression.match(args);
|
||||||
}
|
}
|
||||||
to->externalUpdateFinish();
|
}
|
||||||
return to->toOriginalFile();
|
if (!args.isEmpty() && args != to->getVanillaVersion()->minecraftArguments)
|
||||||
|
{
|
||||||
|
obj.insert("minecraftArguments", args);
|
||||||
|
}
|
||||||
|
if (!tweakers.isEmpty())
|
||||||
|
{
|
||||||
|
obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
|
||||||
|
}
|
||||||
|
if (!m_forge_version->processArguments.isEmpty() &&
|
||||||
|
m_forge_version->processArguments != to->getVanillaVersion()->processArguments)
|
||||||
|
{
|
||||||
|
obj.insert("processArguments", m_forge_version->processArguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.insert("name", QString("Forge"));
|
||||||
|
obj.insert("fileId", id());
|
||||||
|
obj.insert("version", m_forgeVersionString);
|
||||||
|
obj.insert("mcVersion", to->intendedVersionId());
|
||||||
|
|
||||||
|
QFile file(filename(to->instanceRoot()));
|
||||||
|
if (!file.open(QFile::WriteOnly))
|
||||||
|
{
|
||||||
|
QLOG_ERROR() << "Error opening" << file.fileName()
|
||||||
|
<< "for reading:" << file.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
file.write(QJsonDocument(obj).toJson());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -14,17 +14,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "BaseInstaller.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class OneSixVersion;
|
class OneSixVersion;
|
||||||
|
|
||||||
class ForgeInstaller
|
class ForgeInstaller : public BaseInstaller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ForgeInstaller(QString filename, QString universal_url);
|
ForgeInstaller(QString filename, QString universal_url);
|
||||||
|
|
||||||
bool apply(std::shared_ptr<OneSixVersion> to);
|
bool add(OneSixInstance *to) override;
|
||||||
|
|
||||||
|
QString id() const override { return "net.minecraftforge"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// the version, read from the installer
|
// the version, read from the installer
|
||||||
@ -32,5 +37,6 @@ private:
|
|||||||
QString internalPath;
|
QString internalPath;
|
||||||
QString finalPath;
|
QString finalPath;
|
||||||
QString realVersionId;
|
QString realVersionId;
|
||||||
|
QString m_forgeVersionString;
|
||||||
QString m_universal_url;
|
QString m_universal_url;
|
||||||
};
|
};
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "OneSixInstance.h"
|
#include "OneSixInstance.h"
|
||||||
#include "OneSixFTBInstance.h"
|
#include "OneSixFTBInstance.h"
|
||||||
#include "NostalgiaInstance.h"
|
#include "NostalgiaInstance.h"
|
||||||
|
#include "OneSixInstance.h"
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "MinecraftVersion.h"
|
#include "MinecraftVersion.h"
|
||||||
|
|
||||||
@ -50,14 +51,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
|
|||||||
QString inst_type = m_settings->get("InstanceType").toString();
|
QString inst_type = m_settings->get("InstanceType").toString();
|
||||||
|
|
||||||
// FIXME: replace with a map lookup, where instance classes register their types
|
// FIXME: replace with a map lookup, where instance classes register their types
|
||||||
if (inst_type == "Legacy")
|
if (inst_type == "OneSix")
|
||||||
{
|
|
||||||
inst = new LegacyInstance(instDir, m_settings, this);
|
|
||||||
}
|
|
||||||
else if (inst_type == "OneSix")
|
|
||||||
{
|
{
|
||||||
inst = new OneSixInstance(instDir, m_settings, this);
|
inst = new OneSixInstance(instDir, m_settings, this);
|
||||||
}
|
}
|
||||||
|
else if (inst_type == "Legacy")
|
||||||
|
{
|
||||||
|
inst = new LegacyInstance(instDir, m_settings, this);
|
||||||
|
}
|
||||||
else if (inst_type == "Nostalgia")
|
else if (inst_type == "Nostalgia")
|
||||||
{
|
{
|
||||||
inst = new NostalgiaInstance(instDir, m_settings, this);
|
inst = new NostalgiaInstance(instDir, m_settings, this);
|
||||||
@ -101,6 +102,7 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&
|
|||||||
switch (mcVer->type)
|
switch (mcVer->type)
|
||||||
{
|
{
|
||||||
case MinecraftVersion::Legacy:
|
case MinecraftVersion::Legacy:
|
||||||
|
// TODO new instance type
|
||||||
m_settings->set("InstanceType", "Legacy");
|
m_settings->set("InstanceType", "Legacy");
|
||||||
inst = new LegacyInstance(instDir, m_settings, this);
|
inst = new LegacyInstance(instDir, m_settings, this);
|
||||||
inst->setIntendedVersionId(version->descriptor());
|
inst->setIntendedVersionId(version->descriptor());
|
||||||
|
@ -15,12 +15,19 @@
|
|||||||
|
|
||||||
#include "LiteLoaderInstaller.h"
|
#include "LiteLoaderInstaller.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "logger/QsLog.h"
|
||||||
|
|
||||||
#include "OneSixVersion.h"
|
#include "OneSixVersion.h"
|
||||||
#include "OneSixLibrary.h"
|
#include "OneSixLibrary.h"
|
||||||
|
#include "OneSixInstance.h"
|
||||||
|
|
||||||
QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping;
|
QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping;
|
||||||
|
|
||||||
LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion)
|
LiteLoaderInstaller::LiteLoaderInstaller()
|
||||||
|
: BaseInstaller()
|
||||||
{
|
{
|
||||||
if (m_launcherWrapperVersionMapping.isEmpty())
|
if (m_launcherWrapperVersionMapping.isEmpty())
|
||||||
{
|
{
|
||||||
@ -31,72 +38,60 @@ LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LiteLoaderInstaller::canApply() const
|
bool LiteLoaderInstaller::canApply(OneSixInstance *instance) const
|
||||||
{
|
{
|
||||||
return m_launcherWrapperVersionMapping.contains(m_mcVersion);
|
return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LiteLoaderInstaller::apply(std::shared_ptr<OneSixVersion> to)
|
bool LiteLoaderInstaller::add(OneSixInstance *to)
|
||||||
{
|
{
|
||||||
to->externalUpdateStart();
|
if (!BaseInstaller::add(to))
|
||||||
|
|
||||||
applyLaunchwrapper(to);
|
|
||||||
applyLiteLoader(to);
|
|
||||||
|
|
||||||
to->mainClass = "net.minecraft.launchwrapper.Launch";
|
|
||||||
if (!to->minecraftArguments.contains(
|
|
||||||
" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"))
|
|
||||||
{
|
{
|
||||||
to->minecraftArguments.append(
|
return false;
|
||||||
" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
to->externalUpdateFinish();
|
QJsonObject obj;
|
||||||
return to->toOriginalFile();
|
|
||||||
|
obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch"));
|
||||||
|
obj.insert("+tweakers", QJsonArray::fromStringList(QStringList() << "com.mumfrey.liteloader.launch.LiteLoaderTweaker"));
|
||||||
|
obj.insert("order", 10);
|
||||||
|
|
||||||
|
QJsonArray libraries;
|
||||||
|
|
||||||
|
// launchwrapper
|
||||||
|
{
|
||||||
|
OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]);
|
||||||
|
launchwrapperLib.finalize();
|
||||||
|
QJsonObject lwLibObj = launchwrapperLib.toJson();
|
||||||
|
lwLibObj.insert("insert", QString("prepend"));
|
||||||
|
libraries.append(lwLibObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr<OneSixVersion> to)
|
// liteloader
|
||||||
{
|
{
|
||||||
const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion];
|
OneSixLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId());
|
||||||
|
liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/");
|
||||||
QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
|
liteloaderLib.finalize();
|
||||||
while (it.hasNext())
|
QJsonObject llLibObj = liteloaderLib.toJson();
|
||||||
{
|
llLibObj.insert("insert", QString("prepend"));
|
||||||
it.next();
|
llLibObj.insert("MMC-depend", QString("hard"));
|
||||||
if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:"))
|
libraries.append(llLibObj);
|
||||||
{
|
|
||||||
if (it.value()->version() >= intendedVersion)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<OneSixLibrary> lib(new OneSixLibrary(
|
obj.insert("+libraries", libraries);
|
||||||
"net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion]));
|
obj.insert("name", QString("LiteLoader"));
|
||||||
lib->finalize();
|
obj.insert("fileId", id());
|
||||||
to->libraries.prepend(lib);
|
obj.insert("version", to->intendedVersionId());
|
||||||
}
|
obj.insert("mcVersion", to->intendedVersionId());
|
||||||
|
|
||||||
void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr<OneSixVersion> to)
|
QFile file(filename(to->instanceRoot()));
|
||||||
|
if (!file.open(QFile::WriteOnly))
|
||||||
{
|
{
|
||||||
QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
|
QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
|
||||||
while (it.hasNext())
|
return false;
|
||||||
{
|
|
||||||
it.next();
|
|
||||||
if (it.value()->rawName().startsWith("com.mumfrey:liteloader:"))
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
file.write(QJsonDocument(obj).toJson());
|
||||||
|
file.close();
|
||||||
|
|
||||||
std::shared_ptr<OneSixLibrary> lib(
|
return true;
|
||||||
new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion));
|
|
||||||
lib->setBaseUrl("http://dl.liteloader.com/versions/");
|
|
||||||
lib->finalize();
|
|
||||||
to->libraries.prepend(lib);
|
|
||||||
}
|
}
|
||||||
|
@ -14,26 +14,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "BaseInstaller.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class OneSixVersion;
|
class LiteLoaderInstaller : public BaseInstaller
|
||||||
|
|
||||||
class LiteLoaderInstaller
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LiteLoaderInstaller(const QString &mcVersion);
|
LiteLoaderInstaller();
|
||||||
|
|
||||||
bool canApply() const;
|
bool canApply(OneSixInstance *instance) const override;
|
||||||
|
bool add(OneSixInstance *to) override;
|
||||||
bool apply(std::shared_ptr<OneSixVersion> to);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_mcVersion;
|
virtual QString id() const override { return "com.mumfrey.liteloader"; }
|
||||||
|
|
||||||
void applyLaunchwrapper(std::shared_ptr<OneSixVersion> to);
|
|
||||||
void applyLiteLoader(std::shared_ptr<OneSixVersion> to);
|
|
||||||
|
|
||||||
static QMap<QString, QString> m_launcherWrapperVersionMapping;
|
static QMap<QString, QString> m_launcherWrapperVersionMapping;
|
||||||
};
|
};
|
||||||
|
@ -55,15 +55,13 @@ slots:
|
|||||||
setStatus(tr("Installing Forge..."));
|
setStatus(tr("Installing Forge..."));
|
||||||
QString forgePath = entry->getFullPath();
|
QString forgePath = entry->getFullPath();
|
||||||
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||||
if (!instance->reloadFullVersion())
|
if (!instance->reloadVersion())
|
||||||
{
|
{
|
||||||
emitFailed(tr("Couldn't load the version config"));
|
emitFailed(tr("Couldn't load the version config"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance->revertCustomVersion();
|
|
||||||
instance->customizeVersion();
|
|
||||||
auto version = instance->getFullVersion();
|
auto version = instance->getFullVersion();
|
||||||
if (!forge.apply(version))
|
if (!forge.add(instance))
|
||||||
{
|
{
|
||||||
emitFailed(tr("Couldn't install Forge"));
|
emitFailed(tr("Couldn't install Forge"));
|
||||||
return;
|
return;
|
||||||
|
@ -13,32 +13,37 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MultiMC.h"
|
|
||||||
#include "OneSixInstance.h"
|
#include "OneSixInstance.h"
|
||||||
#include "OneSixInstance_p.h"
|
|
||||||
#include "OneSixUpdate.h"
|
|
||||||
#include "MinecraftProcess.h"
|
|
||||||
#include "OneSixVersion.h"
|
|
||||||
#include "JavaChecker.h"
|
|
||||||
#include "logic/icons/IconList.h"
|
|
||||||
|
|
||||||
#include <setting.h>
|
|
||||||
#include <pathutils.h>
|
|
||||||
#include <cmdutils.h>
|
|
||||||
#include <JlCompress.h>
|
|
||||||
#include "gui/dialogs/OneSixModEditDialog.h"
|
|
||||||
#include "logger/QsLog.h"
|
|
||||||
#include "logic/assets/AssetsUtils.h"
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj,
|
#include "OneSixInstance_p.h"
|
||||||
QObject *parent)
|
#include "OneSixUpdate.h"
|
||||||
: BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent)
|
#include "OneSixVersion.h"
|
||||||
|
#include "pathutils.h"
|
||||||
|
#include "logger/QsLog.h"
|
||||||
|
#include "assets/AssetsUtils.h"
|
||||||
|
#include "MultiMC.h"
|
||||||
|
#include "icons/IconList.h"
|
||||||
|
#include "MinecraftProcess.h"
|
||||||
|
#include "gui/dialogs/OneSixModEditDialog.h"
|
||||||
|
|
||||||
|
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
|
||||||
|
: BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
|
||||||
{
|
{
|
||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
d->m_settings->registerSetting("IntendedVersion", "");
|
d->m_settings->registerSetting("IntendedVersion", "");
|
||||||
d->m_settings->registerSetting("ShouldUpdate", false);
|
d->m_settings->registerSetting("ShouldUpdate", false);
|
||||||
reloadFullVersion();
|
d->version.reset(new OneSixVersion(this, this));
|
||||||
|
d->vanillaVersion.reset(new OneSixVersion(this, this));
|
||||||
|
if (QDir(instanceRoot()).exists("version.json"))
|
||||||
|
{
|
||||||
|
reloadVersion();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clearVersion();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Task> OneSixInstance::doUpdate()
|
std::shared_ptr<Task> OneSixInstance::doUpdate()
|
||||||
@ -135,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
|
|||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
auto version = d->version;
|
auto version = d->version;
|
||||||
QString args_pattern = version->minecraftArguments;
|
QString args_pattern = version->minecraftArguments;
|
||||||
|
for (auto tweaker : version->tweakers)
|
||||||
|
{
|
||||||
|
args_pattern += " --tweakClass " + tweaker;
|
||||||
|
}
|
||||||
|
|
||||||
QMap<QString, QString> token_mapping;
|
QMap<QString, QString> token_mapping;
|
||||||
// yggdrasil!
|
// yggdrasil!
|
||||||
@ -267,11 +276,8 @@ bool OneSixInstance::setIntendedVersionId(QString version)
|
|||||||
{
|
{
|
||||||
settings().set("IntendedVersion", version);
|
settings().set("IntendedVersion", version);
|
||||||
setShouldUpdate(true);
|
setShouldUpdate(true);
|
||||||
auto pathCustom = PathCombine(instanceRoot(), "custom.json");
|
QFile::remove(PathCombine(instanceRoot(), "version.json"));
|
||||||
auto pathOrig = PathCombine(instanceRoot(), "version.json");
|
clearVersion();
|
||||||
QFile::remove(pathCustom);
|
|
||||||
QFile::remove(pathOrig);
|
|
||||||
reloadFullVersion();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,9 +303,10 @@ bool OneSixInstance::shouldUpdate() const
|
|||||||
|
|
||||||
bool OneSixInstance::versionIsCustom()
|
bool OneSixInstance::versionIsCustom()
|
||||||
{
|
{
|
||||||
QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
|
QDir patches(PathCombine(instanceRoot(), "patches/"));
|
||||||
QFile versionfile(verpath_custom);
|
return (patches.exists() && patches.count() >= 0)
|
||||||
return versionfile.exists();
|
|| QFile::exists(PathCombine(instanceRoot(), "custom.json"))
|
||||||
|
|| QFile::exists(PathCombine(instanceRoot(), "user.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OneSixInstance::currentVersionId() const
|
QString OneSixInstance::currentVersionId() const
|
||||||
@ -307,62 +314,39 @@ QString OneSixInstance::currentVersionId() const
|
|||||||
return intendedVersionId();
|
return intendedVersionId();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OneSixInstance::customizeVersion()
|
bool OneSixInstance::reloadVersion(QWidget *widgetParent)
|
||||||
{
|
|
||||||
if (!versionIsCustom())
|
|
||||||
{
|
|
||||||
auto pathCustom = PathCombine(instanceRoot(), "custom.json");
|
|
||||||
auto pathOrig = PathCombine(instanceRoot(), "version.json");
|
|
||||||
QFile::copy(pathOrig, pathCustom);
|
|
||||||
return reloadFullVersion();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OneSixInstance::revertCustomVersion()
|
|
||||||
{
|
|
||||||
if (versionIsCustom())
|
|
||||||
{
|
|
||||||
auto path = PathCombine(instanceRoot(), "custom.json");
|
|
||||||
QFile::remove(path);
|
|
||||||
return reloadFullVersion();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OneSixInstance::reloadFullVersion()
|
|
||||||
{
|
{
|
||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
|
|
||||||
QString verpath = PathCombine(instanceRoot(), "version.json");
|
bool ret = d->version->reload(widgetParent);
|
||||||
|
if (ret)
|
||||||
{
|
{
|
||||||
QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
|
ret = d->vanillaVersion->reload(widgetParent, true);
|
||||||
QFile versionfile(verpath_custom);
|
}
|
||||||
if (versionfile.exists())
|
emit versionReloaded();
|
||||||
verpath = verpath_custom;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto version = OneSixVersion::fromFile(verpath);
|
void OneSixInstance::clearVersion()
|
||||||
if (version)
|
|
||||||
{
|
|
||||||
d->version = version;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->version.reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion()
|
|
||||||
{
|
{
|
||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
|
d->version->clear();
|
||||||
|
d->vanillaVersion->clear();
|
||||||
|
emit versionReloaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
|
||||||
|
{
|
||||||
|
I_D(const OneSixInstance);
|
||||||
return d->version;
|
return d->version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<OneSixVersion> OneSixInstance::getVanillaVersion() const
|
||||||
|
{
|
||||||
|
I_D(const OneSixInstance);
|
||||||
|
return d->vanillaVersion;
|
||||||
|
}
|
||||||
|
|
||||||
QString OneSixInstance::defaultBaseJar() const
|
QString OneSixInstance::defaultBaseJar() const
|
||||||
{
|
{
|
||||||
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
|
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
|
||||||
|
@ -15,14 +15,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
class OneSixVersion;
|
#include "OneSixVersion.h"
|
||||||
class Task;
|
#include "ModList.h"
|
||||||
class ModList;
|
|
||||||
|
|
||||||
class OneSixInstance : public BaseInstance
|
class OneSixInstance : public BaseInstance
|
||||||
{
|
{
|
||||||
@ -55,14 +51,14 @@ public:
|
|||||||
|
|
||||||
virtual QDialog *createModEditDialog(QWidget *parent) override;
|
virtual QDialog *createModEditDialog(QWidget *parent) override;
|
||||||
|
|
||||||
/// reload the full version json file. return true on success!
|
/// reload the full version json files. return true on success!
|
||||||
bool reloadFullVersion();
|
bool reloadVersion(QWidget *widgetParent = 0);
|
||||||
|
/// clears all version information in preparation for an update
|
||||||
|
void clearVersion();
|
||||||
/// get the current full version info
|
/// get the current full version info
|
||||||
std::shared_ptr<OneSixVersion> getFullVersion();
|
std::shared_ptr<OneSixVersion> getFullVersion() const;
|
||||||
/// revert the current custom version back to base
|
/// gets the current version info, but only for version.json
|
||||||
bool revertCustomVersion();
|
std::shared_ptr<OneSixVersion> getVanillaVersion() const;
|
||||||
/// customize the current base version
|
|
||||||
bool customizeVersion();
|
|
||||||
/// is the current version original, or custom?
|
/// is the current version original, or custom?
|
||||||
virtual bool versionIsCustom() override;
|
virtual bool versionIsCustom() override;
|
||||||
|
|
||||||
@ -72,6 +68,9 @@ public:
|
|||||||
virtual bool menuActionEnabled(QString action_name) const override;
|
virtual bool menuActionEnabled(QString action_name) const override;
|
||||||
virtual QString getStatusbarDescription() override;
|
virtual QString getStatusbarDescription() override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void versionReloaded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList processMinecraftArgs(AuthSessionPtr account);
|
QStringList processMinecraftArgs(AuthSessionPtr account);
|
||||||
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
|
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
|
||||||
|
@ -15,16 +15,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include "BaseInstance_p.h"
|
||||||
|
#include "OneSixVersion.h"
|
||||||
#include "logic/BaseInstance_p.h"
|
#include "ModList.h"
|
||||||
#include "logic/OneSixVersion.h"
|
|
||||||
#include "logic/OneSixLibrary.h"
|
|
||||||
#include "logic/ModList.h"
|
|
||||||
|
|
||||||
struct OneSixInstancePrivate : public BaseInstancePrivate
|
struct OneSixInstancePrivate : public BaseInstancePrivate
|
||||||
{
|
{
|
||||||
std::shared_ptr<OneSixVersion> version;
|
std::shared_ptr<OneSixVersion> version;
|
||||||
|
std::shared_ptr<OneSixVersion> vanillaVersion;
|
||||||
std::shared_ptr<ModList> loader_mod_list;
|
std::shared_ptr<ModList> loader_mod_list;
|
||||||
std::shared_ptr<ModList> resource_pack_list;
|
std::shared_ptr<ModList> resource_pack_list;
|
||||||
};
|
};
|
@ -46,7 +46,7 @@ void OneSixLibrary::finalize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_decentname = parts[1];
|
m_decentname = parts[1];
|
||||||
m_decentversion = parts[2];
|
m_decentversion = minVersion = parts[2];
|
||||||
m_storage_path = relative;
|
m_storage_path = relative;
|
||||||
m_download_url = m_base_url + relative;
|
m_download_url = m_base_url + relative;
|
||||||
|
|
||||||
@ -76,11 +76,11 @@ void OneSixLibrary::finalize()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixLibrary::setName(QString name)
|
void OneSixLibrary::setName(const QString &name)
|
||||||
{
|
{
|
||||||
m_name = name;
|
m_name = name;
|
||||||
}
|
}
|
||||||
void OneSixLibrary::setBaseUrl(QString base_url)
|
void OneSixLibrary::setBaseUrl(const QString &base_url)
|
||||||
{
|
{
|
||||||
m_base_url = base_url;
|
m_base_url = base_url;
|
||||||
}
|
}
|
||||||
@ -88,50 +88,54 @@ void OneSixLibrary::setIsNative()
|
|||||||
{
|
{
|
||||||
m_is_native = true;
|
m_is_native = true;
|
||||||
}
|
}
|
||||||
void OneSixLibrary::addNative(OpSys os, QString suffix)
|
void OneSixLibrary::addNative(OpSys os, const QString &suffix)
|
||||||
{
|
{
|
||||||
m_is_native = true;
|
m_is_native = true;
|
||||||
m_native_suffixes[os] = suffix;
|
m_native_suffixes[os] = suffix;
|
||||||
}
|
}
|
||||||
|
void OneSixLibrary::clearSuffixes()
|
||||||
|
{
|
||||||
|
m_native_suffixes.clear();
|
||||||
|
}
|
||||||
void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
|
void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
|
||||||
{
|
{
|
||||||
m_rules = rules;
|
m_rules = rules;
|
||||||
}
|
}
|
||||||
bool OneSixLibrary::isActive()
|
bool OneSixLibrary::isActive() const
|
||||||
{
|
{
|
||||||
return m_is_active;
|
return m_is_active;
|
||||||
}
|
}
|
||||||
bool OneSixLibrary::isNative()
|
bool OneSixLibrary::isNative() const
|
||||||
{
|
{
|
||||||
return m_is_native;
|
return m_is_native;
|
||||||
}
|
}
|
||||||
QString OneSixLibrary::downloadUrl()
|
QString OneSixLibrary::downloadUrl() const
|
||||||
{
|
{
|
||||||
if (m_absolute_url.size())
|
if (m_absolute_url.size())
|
||||||
return m_absolute_url;
|
return m_absolute_url;
|
||||||
return m_download_url;
|
return m_download_url;
|
||||||
}
|
}
|
||||||
QString OneSixLibrary::storagePath()
|
QString OneSixLibrary::storagePath() const
|
||||||
{
|
{
|
||||||
return m_storage_path;
|
return m_storage_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixLibrary::setAbsoluteUrl(QString absolute_url)
|
void OneSixLibrary::setAbsoluteUrl(const QString &absolute_url)
|
||||||
{
|
{
|
||||||
m_absolute_url = absolute_url;
|
m_absolute_url = absolute_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OneSixLibrary::absoluteUrl()
|
QString OneSixLibrary::absoluteUrl() const
|
||||||
{
|
{
|
||||||
return m_absolute_url;
|
return m_absolute_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixLibrary::setHint(QString hint)
|
void OneSixLibrary::setHint(const QString &hint)
|
||||||
{
|
{
|
||||||
m_hint = hint;
|
m_hint = hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OneSixLibrary::hint()
|
QString OneSixLibrary::hint() const
|
||||||
{
|
{
|
||||||
return m_hint;
|
return m_hint;
|
||||||
}
|
}
|
||||||
@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson()
|
|||||||
libRoot.insert("MMC-hint", m_hint);
|
libRoot.insert("MMC-hint", m_hint);
|
||||||
if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
|
if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
|
||||||
m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
|
m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
|
||||||
m_base_url != "https://" + URLConstants::LIBRARY_BASE)
|
m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
|
||||||
|
{
|
||||||
libRoot.insert("url", m_base_url);
|
libRoot.insert("url", m_base_url);
|
||||||
|
}
|
||||||
if (isNative() && m_native_suffixes.size())
|
if (isNative() && m_native_suffixes.size())
|
||||||
{
|
{
|
||||||
QJsonObject nativeList;
|
QJsonObject nativeList;
|
||||||
|
@ -60,12 +60,21 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
QStringList extract_excludes;
|
QStringList extract_excludes;
|
||||||
|
QString minVersion;
|
||||||
|
|
||||||
|
enum DependType
|
||||||
|
{
|
||||||
|
Soft,
|
||||||
|
Hard
|
||||||
|
};
|
||||||
|
DependType dependType;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
OneSixLibrary(QString name)
|
OneSixLibrary(const QString &name, const DependType type = Soft)
|
||||||
{
|
{
|
||||||
m_name = name;
|
m_name = name;
|
||||||
|
dependType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the raw name field
|
/// Returns the raw name field
|
||||||
@ -84,48 +93,50 @@ public:
|
|||||||
void finalize();
|
void finalize();
|
||||||
|
|
||||||
/// Set the library composite name
|
/// Set the library composite name
|
||||||
void setName(QString name);
|
void setName(const QString &name);
|
||||||
/// get a decent-looking name
|
/// get a decent-looking name
|
||||||
QString name()
|
QString name() const
|
||||||
{
|
{
|
||||||
return m_decentname;
|
return m_decentname;
|
||||||
}
|
}
|
||||||
/// get a decent-looking version
|
/// get a decent-looking version
|
||||||
QString version()
|
QString version() const
|
||||||
{
|
{
|
||||||
return m_decentversion;
|
return m_decentversion;
|
||||||
}
|
}
|
||||||
/// what kind of library is it? (for display)
|
/// what kind of library is it? (for display)
|
||||||
QString type()
|
QString type() const
|
||||||
{
|
{
|
||||||
return m_decenttype;
|
return m_decenttype;
|
||||||
}
|
}
|
||||||
/// Set the url base for downloads
|
/// Set the url base for downloads
|
||||||
void setBaseUrl(QString base_url);
|
void setBaseUrl(const QString &base_url);
|
||||||
|
|
||||||
/// Call this to mark the library as 'native' (it's a zip archive with DLLs)
|
/// Call this to mark the library as 'native' (it's a zip archive with DLLs)
|
||||||
void setIsNative();
|
void setIsNative();
|
||||||
/// Attach a name suffix to the specified OS native
|
/// Attach a name suffix to the specified OS native
|
||||||
void addNative(OpSys os, QString suffix);
|
void addNative(OpSys os, const QString &suffix);
|
||||||
|
/// Clears all suffixes
|
||||||
|
void clearSuffixes();
|
||||||
/// Set the load rules
|
/// Set the load rules
|
||||||
void setRules(QList<std::shared_ptr<Rule>> rules);
|
void setRules(QList<std::shared_ptr<Rule>> rules);
|
||||||
|
|
||||||
/// Returns true if the library should be loaded (or extracted, in case of natives)
|
/// Returns true if the library should be loaded (or extracted, in case of natives)
|
||||||
bool isActive();
|
bool isActive() const;
|
||||||
/// Returns true if the library is native
|
/// Returns true if the library is native
|
||||||
bool isNative();
|
bool isNative() const;
|
||||||
/// Get the URL to download the library from
|
/// Get the URL to download the library from
|
||||||
QString downloadUrl();
|
QString downloadUrl() const;
|
||||||
/// Get the relative path where the library should be saved
|
/// Get the relative path where the library should be saved
|
||||||
QString storagePath();
|
QString storagePath() const;
|
||||||
|
|
||||||
/// set an absolute URL for the library. This is an MMC extension.
|
/// set an absolute URL for the library. This is an MMC extension.
|
||||||
void setAbsoluteUrl(QString absolute_url);
|
void setAbsoluteUrl(const QString &absolute_url);
|
||||||
QString absoluteUrl();
|
QString absoluteUrl() const;
|
||||||
|
|
||||||
/// set a hint about how to treat the library. This is an MMC extension.
|
/// set a hint about how to treat the library. This is an MMC extension.
|
||||||
void setHint(QString hint);
|
void setHint(const QString &hint);
|
||||||
QString hint();
|
QString hint() const;
|
||||||
|
|
||||||
bool extractTo(QString target_dir);
|
bool extractTo(QString target_dir);
|
||||||
bool filesExist();
|
bool filesExist();
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
#include "OneSixRule.h"
|
#include "OneSixRule.h"
|
||||||
|
|
||||||
QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules)
|
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
|
||||||
{
|
{
|
||||||
QList<std::shared_ptr<Rule>> rules;
|
QList<std::shared_ptr<Rule>> rules;
|
||||||
auto rulesVal = objectWithRules.value("rules");
|
auto rulesVal = objectWithRules.value("rules");
|
||||||
|
@ -27,7 +27,7 @@ enum RuleAction
|
|||||||
};
|
};
|
||||||
|
|
||||||
RuleAction RuleAction_fromString(QString);
|
RuleAction RuleAction_fromString(QString);
|
||||||
QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules);
|
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
|
||||||
|
|
||||||
class Rule
|
class Rule
|
||||||
{
|
{
|
||||||
|
@ -131,7 +131,7 @@ void OneSixUpdate::versionFileFinished()
|
|||||||
{
|
{
|
||||||
finfo.remove();
|
finfo.remove();
|
||||||
}
|
}
|
||||||
inst->reloadFullVersion();
|
inst->reloadVersion();
|
||||||
|
|
||||||
jarlibStart();
|
jarlibStart();
|
||||||
}
|
}
|
||||||
@ -229,7 +229,7 @@ void OneSixUpdate::jarlibStart()
|
|||||||
setStatus(tr("Getting the library files from Mojang..."));
|
setStatus(tr("Getting the library files from Mojang..."));
|
||||||
QLOG_INFO() << m_inst->name() << ": downloading libraries";
|
QLOG_INFO() << m_inst->name() << ": downloading libraries";
|
||||||
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
OneSixInstance *inst = (OneSixInstance *)m_inst;
|
||||||
bool successful = inst->reloadFullVersion();
|
bool successful = inst->reloadVersion();
|
||||||
if (!successful)
|
if (!successful)
|
||||||
{
|
{
|
||||||
emitFailed("Failed to load the version description file. It might be "
|
emitFailed("Failed to load the version description file. It might be "
|
||||||
|
@ -13,223 +13,90 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "logic/OneSixVersion.h"
|
#include "OneSixVersion.h"
|
||||||
#include "logic/OneSixLibrary.h"
|
|
||||||
#include "logic/OneSixRule.h"
|
|
||||||
|
|
||||||
#include "logger/QsLog.h"
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root,
|
#include "OneSixVersionBuilder.h"
|
||||||
std::shared_ptr<OneSixVersion> fullVersion)
|
|
||||||
{
|
|
||||||
fullVersion->id = root.value("id").toString();
|
|
||||||
|
|
||||||
fullVersion->mainClass = root.value("mainClass").toString();
|
OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent)
|
||||||
auto procArgsValue = root.value("processArguments");
|
: QAbstractListModel(parent), m_instance(instance)
|
||||||
if (procArgsValue.isString())
|
|
||||||
{
|
{
|
||||||
fullVersion->processArguments = procArgsValue.toString();
|
clear();
|
||||||
QString toCompare = fullVersion->processArguments.toLower();
|
|
||||||
if (toCompare == "legacy")
|
|
||||||
{
|
|
||||||
fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}";
|
|
||||||
}
|
|
||||||
else if (toCompare == "username_session")
|
|
||||||
{
|
|
||||||
fullVersion->minecraftArguments =
|
|
||||||
"--username ${auth_player_name} --session ${auth_session}";
|
|
||||||
}
|
|
||||||
else if (toCompare == "username_session_version")
|
|
||||||
{
|
|
||||||
fullVersion->minecraftArguments = "--username ${auth_player_name} "
|
|
||||||
"--session ${auth_session} "
|
|
||||||
"--version ${profile_name}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto minecraftArgsValue = root.value("minecraftArguments");
|
bool OneSixVersion::reload(QWidget *widgetParent, const bool onlyVanilla)
|
||||||
if (minecraftArgsValue.isString())
|
|
||||||
{
|
{
|
||||||
fullVersion->minecraftArguments = minecraftArgsValue.toString();
|
beginResetModel();
|
||||||
|
bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla);
|
||||||
|
endResetModel();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto minecraftTypeValue = root.value("type");
|
void OneSixVersion::clear()
|
||||||
if (minecraftTypeValue.isString())
|
|
||||||
{
|
{
|
||||||
fullVersion->type = minecraftTypeValue.toString();
|
beginResetModel();
|
||||||
|
id.clear();
|
||||||
|
time.clear();
|
||||||
|
releaseTime.clear();
|
||||||
|
type.clear();
|
||||||
|
assets.clear();
|
||||||
|
processArguments.clear();
|
||||||
|
minecraftArguments.clear();
|
||||||
|
minimumLauncherVersion = 0xDEADBEAF;
|
||||||
|
mainClass.clear();
|
||||||
|
libraries.clear();
|
||||||
|
tweakers.clear();
|
||||||
|
versionFiles.clear();
|
||||||
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
fullVersion->releaseTime = root.value("releaseTime").toString();
|
void OneSixVersion::dump() const
|
||||||
fullVersion->time = root.value("time").toString();
|
|
||||||
|
|
||||||
auto assetsID = root.value("assets");
|
|
||||||
if (assetsID.isString())
|
|
||||||
{
|
{
|
||||||
fullVersion->assets = assetsID.toString();
|
qDebug().nospace() << "OneSixVersion("
|
||||||
|
<< "\n\tid=" << id
|
||||||
|
<< "\n\ttime=" << time
|
||||||
|
<< "\n\treleaseTime=" << releaseTime
|
||||||
|
<< "\n\ttype=" << type
|
||||||
|
<< "\n\tassets=" << assets
|
||||||
|
<< "\n\tprocessArguments=" << processArguments
|
||||||
|
<< "\n\tminecraftArguments=" << minecraftArguments
|
||||||
|
<< "\n\tminimumLauncherVersion=" << minimumLauncherVersion
|
||||||
|
<< "\n\tmainClass=" << mainClass
|
||||||
|
<< "\n\tlibraries=";
|
||||||
|
for (auto lib : libraries)
|
||||||
|
{
|
||||||
|
qDebug().nospace() << "\n\t\t" << lib.get();
|
||||||
}
|
}
|
||||||
else
|
qDebug().nospace() << "\n)";
|
||||||
{
|
|
||||||
fullVersion->assets = "legacy";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QLOG_DEBUG() << "Assets version:" << fullVersion->assets;
|
bool OneSixVersion::canRemove(const int index) const
|
||||||
|
|
||||||
// Iterate through the list, if it's a list.
|
|
||||||
auto librariesValue = root.value("libraries");
|
|
||||||
if (!librariesValue.isArray())
|
|
||||||
return fullVersion;
|
|
||||||
|
|
||||||
QJsonArray libList = root.value("libraries").toArray();
|
|
||||||
for (auto libVal : libList)
|
|
||||||
{
|
{
|
||||||
if (!libVal.isObject())
|
if (index < versionFiles.size())
|
||||||
{
|
{
|
||||||
continue;
|
return versionFiles.at(index).id != "org.multimc.version.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject libObj = libVal.toObject();
|
|
||||||
|
|
||||||
// Library name
|
|
||||||
auto nameVal = libObj.value("name");
|
|
||||||
if (!nameVal.isString())
|
|
||||||
continue;
|
|
||||||
std::shared_ptr<OneSixLibrary> library(new OneSixLibrary(nameVal.toString()));
|
|
||||||
|
|
||||||
auto urlVal = libObj.value("url");
|
|
||||||
if (urlVal.isString())
|
|
||||||
{
|
|
||||||
library->setBaseUrl(urlVal.toString());
|
|
||||||
}
|
|
||||||
auto hintVal = libObj.value("MMC-hint");
|
|
||||||
if (hintVal.isString())
|
|
||||||
{
|
|
||||||
library->setHint(hintVal.toString());
|
|
||||||
}
|
|
||||||
auto urlAbsVal = libObj.value("MMC-absoluteUrl");
|
|
||||||
auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility
|
|
||||||
if (urlAbsVal.isString())
|
|
||||||
{
|
|
||||||
library->setAbsoluteUrl(urlAbsVal.toString());
|
|
||||||
}
|
|
||||||
else if (urlAbsuVal.isString())
|
|
||||||
{
|
|
||||||
library->setAbsoluteUrl(urlAbsuVal.toString());
|
|
||||||
}
|
|
||||||
// Extract excludes (if any)
|
|
||||||
auto extractVal = libObj.value("extract");
|
|
||||||
if (extractVal.isObject())
|
|
||||||
{
|
|
||||||
QStringList excludes;
|
|
||||||
auto extractObj = extractVal.toObject();
|
|
||||||
auto excludesVal = extractObj.value("exclude");
|
|
||||||
if (excludesVal.isArray())
|
|
||||||
{
|
|
||||||
auto excludesList = excludesVal.toArray();
|
|
||||||
for (auto excludeVal : excludesList)
|
|
||||||
{
|
|
||||||
if (excludeVal.isString())
|
|
||||||
excludes.append(excludeVal.toString());
|
|
||||||
}
|
|
||||||
library->extract_excludes = excludes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto nativesVal = libObj.value("natives");
|
|
||||||
if (nativesVal.isObject())
|
|
||||||
{
|
|
||||||
library->setIsNative();
|
|
||||||
auto nativesObj = nativesVal.toObject();
|
|
||||||
auto iter = nativesObj.begin();
|
|
||||||
while (iter != nativesObj.end())
|
|
||||||
{
|
|
||||||
auto osType = OpSys_fromString(iter.key());
|
|
||||||
if (osType == Os_Other)
|
|
||||||
continue;
|
|
||||||
if (!iter.value().isString())
|
|
||||||
continue;
|
|
||||||
library->addNative(osType, iter.value().toString());
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
library->setRules(rulesFromJsonV4(libObj));
|
|
||||||
library->finalize();
|
|
||||||
fullVersion->libraries.append(library);
|
|
||||||
}
|
|
||||||
return fullVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root)
|
|
||||||
{
|
|
||||||
std::shared_ptr<OneSixVersion> readVersion(new OneSixVersion());
|
|
||||||
int launcher_ver = readVersion->minimumLauncherVersion =
|
|
||||||
root.value("minimumLauncherVersion").toDouble();
|
|
||||||
|
|
||||||
// ADD MORE HERE :D
|
|
||||||
if (launcher_ver > 0 && launcher_ver <= 13)
|
|
||||||
return fromJsonV4(root, readVersion);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::shared_ptr<OneSixVersion>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OneSixVersion> OneSixVersion::fromFile(QString filepath)
|
|
||||||
{
|
|
||||||
QFile file(filepath);
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
return std::shared_ptr<OneSixVersion>();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto data = file.readAll();
|
|
||||||
QJsonParseError jsonError;
|
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
|
||||||
|
|
||||||
if (jsonError.error != QJsonParseError::NoError)
|
|
||||||
{
|
|
||||||
return std::shared_ptr<OneSixVersion>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!jsonDoc.isObject())
|
|
||||||
{
|
|
||||||
return std::shared_ptr<OneSixVersion>();
|
|
||||||
}
|
|
||||||
QJsonObject root = jsonDoc.object();
|
|
||||||
auto version = fromJson(root);
|
|
||||||
if (version)
|
|
||||||
version->original_file = filepath;
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OneSixVersion::toOriginalFile()
|
|
||||||
{
|
|
||||||
if (original_file.isEmpty())
|
|
||||||
return false;
|
|
||||||
QSaveFile file(original_file);
|
|
||||||
if (!file.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// serialize base attributes (those we care about anyway)
|
|
||||||
QJsonObject root;
|
QString OneSixVersion::versionFileId(const int index) const
|
||||||
root.insert("minecraftArguments", minecraftArguments);
|
|
||||||
root.insert("mainClass", mainClass);
|
|
||||||
root.insert("minimumLauncherVersion", minimumLauncherVersion);
|
|
||||||
root.insert("time", time);
|
|
||||||
root.insert("id", id);
|
|
||||||
root.insert("type", type);
|
|
||||||
// screw processArguments
|
|
||||||
root.insert("releaseTime", releaseTime);
|
|
||||||
QJsonArray libarray;
|
|
||||||
for (const auto &lib : libraries)
|
|
||||||
{
|
{
|
||||||
libarray.append(lib->toJson());
|
if (index < 0 || index >= versionFiles.size())
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
if (libarray.count())
|
return versionFiles.at(index).id;
|
||||||
root.insert("libraries", libarray);
|
}
|
||||||
QJsonDocument doc(root);
|
|
||||||
file.write(doc.toJson());
|
bool OneSixVersion::remove(const int index)
|
||||||
return file.commit();
|
{
|
||||||
|
if (canRemove(index))
|
||||||
|
{
|
||||||
|
return QFile::remove(versionFiles.at(index).filename);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs()
|
QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs()
|
||||||
@ -258,14 +125,14 @@ QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs()
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixVersion::externalUpdateStart()
|
std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
std::shared_ptr<OneSixVersion> version(new OneSixVersion(0));
|
||||||
|
if (OneSixVersionBuilder::read(version.get(), obj))
|
||||||
|
{
|
||||||
|
return version;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
void OneSixVersion::externalUpdateFinish()
|
|
||||||
{
|
|
||||||
endResetModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant OneSixVersion::data(const QModelIndex &index, int role) const
|
QVariant OneSixVersion::data(const QModelIndex &index, int role) const
|
||||||
@ -276,7 +143,7 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
|
|||||||
int row = index.row();
|
int row = index.row();
|
||||||
int column = index.column();
|
int column = index.column();
|
||||||
|
|
||||||
if (row < 0 || row >= libraries.size())
|
if (row < 0 || row >= versionFiles.size())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
if (role == Qt::DisplayRole)
|
if (role == Qt::DisplayRole)
|
||||||
@ -284,11 +151,9 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
|
|||||||
switch (column)
|
switch (column)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
return libraries[row]->name();
|
return versionFiles.at(row).name;
|
||||||
case 1:
|
case 1:
|
||||||
return libraries[row]->type();
|
return versionFiles.at(row).version;
|
||||||
case 2:
|
|
||||||
return libraries[row]->version();
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -296,45 +161,61 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
{
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return tr("Name");
|
||||||
|
case 1:
|
||||||
|
return tr("Version");
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
|
Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return Qt::NoItemFlags;
|
return Qt::NoItemFlags;
|
||||||
int row = index.row();
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||||
if (libraries[row]->isActive())
|
|
||||||
{
|
|
||||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Qt::ItemNeverHasChildren;
|
|
||||||
}
|
|
||||||
// return QAbstractListModel::flags(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
|
|
||||||
{
|
|
||||||
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
|
|
||||||
return QVariant();
|
|
||||||
switch (section)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
return QString("Name");
|
|
||||||
case 1:
|
|
||||||
return QString("Type");
|
|
||||||
case 2:
|
|
||||||
return QString("Version");
|
|
||||||
default:
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int OneSixVersion::rowCount(const QModelIndex &parent) const
|
int OneSixVersion::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return libraries.size();
|
return versionFiles.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int OneSixVersion::columnCount(const QModelIndex &parent) const
|
int OneSixVersion::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return 3;
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug &dbg, const OneSixVersion *version)
|
||||||
|
{
|
||||||
|
version->dump();
|
||||||
|
return dbg.maybeSpace();
|
||||||
|
}
|
||||||
|
QDebug operator<<(QDebug &dbg, const OneSixLibrary *library)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "OneSixLibrary("
|
||||||
|
<< "\n\t\t\trawName=" << library->rawName()
|
||||||
|
<< "\n\t\t\tname=" << library->name()
|
||||||
|
<< "\n\t\t\tversion=" << library->version()
|
||||||
|
<< "\n\t\t\ttype=" << library->type()
|
||||||
|
<< "\n\t\t\tisActive=" << library->isActive()
|
||||||
|
<< "\n\t\t\tisNative=" << library->isNative()
|
||||||
|
<< "\n\t\t\tdownloadUrl=" << library->downloadUrl()
|
||||||
|
<< "\n\t\t\tstoragePath=" << library->storagePath()
|
||||||
|
<< "\n\t\t\tabsolutePath=" << library->absoluteUrl()
|
||||||
|
<< "\n\t\t\thint=" << library->hint();
|
||||||
|
dbg.nospace() << "\n\t\t)";
|
||||||
|
return dbg.maybeSpace();
|
||||||
}
|
}
|
||||||
|
@ -14,40 +14,50 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QtCore>
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class OneSixLibrary;
|
#include "OneSixLibrary.h"
|
||||||
|
|
||||||
|
class OneSixInstance;
|
||||||
|
|
||||||
class OneSixVersion : public QAbstractListModel
|
class OneSixVersion : public QAbstractListModel
|
||||||
{
|
{
|
||||||
// Things required to implement the Qt list model
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0);
|
||||||
|
|
||||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
|
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
virtual QVariant headerData(int section, Qt::Orientation orientation,
|
|
||||||
int role = Qt::DisplayRole) const;
|
|
||||||
virtual int columnCount(const QModelIndex &parent) const;
|
virtual int columnCount(const QModelIndex &parent) const;
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
|
||||||
// serialization/deserialization
|
bool reload(QWidget *widgetParent, const bool onlyVanilla = false);
|
||||||
public:
|
void clear();
|
||||||
bool toOriginalFile();
|
|
||||||
static std::shared_ptr<OneSixVersion> fromJson(QJsonObject root);
|
void dump() const;
|
||||||
static std::shared_ptr<OneSixVersion> fromFile(QString filepath);
|
|
||||||
|
bool canRemove(const int index) const;
|
||||||
|
|
||||||
|
QString versionFileId(const int index) const;
|
||||||
|
|
||||||
|
public
|
||||||
|
slots:
|
||||||
|
bool remove(const int index);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
|
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
|
||||||
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
|
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
|
||||||
// called when something starts/stops messing with the object
|
|
||||||
// FIXME: these are ugly in every possible way.
|
static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj);
|
||||||
void externalUpdateStart();
|
|
||||||
void externalUpdateFinish();
|
|
||||||
|
|
||||||
// data members
|
// data members
|
||||||
public:
|
public:
|
||||||
/// file this was read from. blank, if none
|
|
||||||
QString original_file;
|
|
||||||
/// the ID - determines which jar to use! ACTUALLY IMPORTANT!
|
/// the ID - determines which jar to use! ACTUALLY IMPORTANT!
|
||||||
QString id;
|
QString id;
|
||||||
/// Last updated time - as a string
|
/// Last updated time - as a string
|
||||||
@ -75,6 +85,10 @@ public:
|
|||||||
* writing)
|
* writing)
|
||||||
*/
|
*/
|
||||||
int minimumLauncherVersion = 0xDEADBEEF;
|
int minimumLauncherVersion = 0xDEADBEEF;
|
||||||
|
/**
|
||||||
|
* A list of all tweaker classes
|
||||||
|
*/
|
||||||
|
QStringList tweakers;
|
||||||
/**
|
/**
|
||||||
* The main class to load first
|
* The main class to load first
|
||||||
*/
|
*/
|
||||||
@ -103,4 +117,21 @@ public:
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// QList<Rule> rules;
|
// QList<Rule> rules;
|
||||||
|
|
||||||
|
struct VersionFile
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString id;
|
||||||
|
QString version;
|
||||||
|
QString mcVersion;
|
||||||
|
QString filename;
|
||||||
|
int order;
|
||||||
};
|
};
|
||||||
|
QList<VersionFile> versionFiles;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OneSixInstance *m_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug &dbg, const OneSixVersion *version);
|
||||||
|
QDebug operator<<(QDebug &dbg, const OneSixLibrary *library);
|
||||||
|
1077
logic/OneSixVersionBuilder.cpp
Normal file
1077
logic/OneSixVersionBuilder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
47
logic/OneSixVersionBuilder.h
Normal file
47
logic/OneSixVersionBuilder.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* Copyright 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
class OneSixVersion;
|
||||||
|
class OneSixInstance;
|
||||||
|
class QWidget;
|
||||||
|
class QJsonObject;
|
||||||
|
class QFileInfo;
|
||||||
|
class VersionFile;
|
||||||
|
|
||||||
|
class OneSixVersionBuilder
|
||||||
|
{
|
||||||
|
OneSixVersionBuilder();
|
||||||
|
public:
|
||||||
|
static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla);
|
||||||
|
static bool read(OneSixVersion *version, const QJsonObject &obj);
|
||||||
|
static QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
|
||||||
|
static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
|
||||||
|
|
||||||
|
private:
|
||||||
|
OneSixVersion *m_version;
|
||||||
|
OneSixInstance *m_instance;
|
||||||
|
QWidget *m_widgetParent;
|
||||||
|
|
||||||
|
bool build(const bool onlyVanilla);
|
||||||
|
bool read(const QJsonObject &obj);
|
||||||
|
|
||||||
|
bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out);
|
||||||
|
|
||||||
|
};
|
@ -33,6 +33,7 @@
|
|||||||
#include "logic/BaseInstance.h"
|
#include "logic/BaseInstance.h"
|
||||||
#include "logic/InstanceFactory.h"
|
#include "logic/InstanceFactory.h"
|
||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
|
#include <gui/groupview/GroupView.h>
|
||||||
|
|
||||||
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||||
|
|
||||||
@ -46,6 +47,13 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent)
|
|||||||
QDir::current().mkpath(m_instDir);
|
QDir::current().mkpath(m_instDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME HACK: instances sometimes need to be created at launch. They need the versions for
|
||||||
|
* that.
|
||||||
|
*
|
||||||
|
* Remove this. it has no business of reloading the whole list. The instances which
|
||||||
|
* need it should track such events themselves and CHANGE THEIR DATA ONLY!
|
||||||
|
*/
|
||||||
connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this,
|
connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this,
|
||||||
&InstanceList::loadList);
|
&InstanceList::loadList);
|
||||||
}
|
}
|
||||||
@ -96,8 +104,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
|
|||||||
return MMC->icons()->getIcon(key);
|
return MMC->icons()->getIcon(key);
|
||||||
}
|
}
|
||||||
// for now.
|
// for now.
|
||||||
case KCategorizedSortFilterProxyModel::CategorySortRole:
|
case GroupViewRoles::GroupRole:
|
||||||
case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
|
|
||||||
{
|
{
|
||||||
return pdata->group();
|
return pdata->group();
|
||||||
}
|
}
|
||||||
@ -282,16 +289,7 @@ void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FTBRecord
|
QList<FTBRecord> InstanceList::discoverFTBInstances()
|
||||||
{
|
|
||||||
QString dir;
|
|
||||||
QString name;
|
|
||||||
QString logo;
|
|
||||||
QString mcVersion;
|
|
||||||
QString description;
|
|
||||||
};
|
|
||||||
|
|
||||||
void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|
||||||
{
|
{
|
||||||
QList<FTBRecord> records;
|
QList<FTBRecord> records;
|
||||||
QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString());
|
QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString());
|
||||||
@ -300,12 +298,12 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
{
|
{
|
||||||
QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your "
|
QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your "
|
||||||
"settings.";
|
"settings.";
|
||||||
return;
|
return records;
|
||||||
}
|
}
|
||||||
else if (!dataDir.exists())
|
else if (!dataDir.exists())
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
|
QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
|
||||||
return;
|
return records;
|
||||||
}
|
}
|
||||||
dir.cd("ModPacks");
|
dir.cd("ModPacks");
|
||||||
auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name);
|
auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name);
|
||||||
@ -331,11 +329,15 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
{
|
{
|
||||||
QXmlStreamAttributes attrs = reader.attributes();
|
QXmlStreamAttributes attrs = reader.attributes();
|
||||||
FTBRecord record;
|
FTBRecord record;
|
||||||
record.dir = attrs.value("dir").toString();
|
record.dirName = attrs.value("dir").toString();
|
||||||
QDir test(dataDir.absoluteFilePath(record.dir));
|
record.instanceDir = dataDir.absoluteFilePath(record.dirName);
|
||||||
|
record.templateDir = dir.absoluteFilePath(record.dirName);
|
||||||
|
QDir test(record.instanceDir);
|
||||||
if (!test.exists())
|
if (!test.exists())
|
||||||
continue;
|
continue;
|
||||||
record.name = attrs.value("name").toString();
|
record.name = attrs.value("name").toString();
|
||||||
|
if(record.name.contains("voxel", Qt::CaseInsensitive))
|
||||||
|
continue;
|
||||||
record.logo = attrs.value("logo").toString();
|
record.logo = attrs.value("logo").toString();
|
||||||
record.mcVersion = attrs.value("mcVersion").toString();
|
record.mcVersion = attrs.value("mcVersion").toString();
|
||||||
record.description = attrs.value("description").toString();
|
record.description = attrs.value("description").toString();
|
||||||
@ -353,7 +355,13 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
}
|
}
|
||||||
f.close();
|
f.close();
|
||||||
}
|
}
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceList::loadFTBInstances(QMap<QString, QString> &groupMap,
|
||||||
|
QList<InstancePtr> &tempList)
|
||||||
|
{
|
||||||
|
auto records = discoverFTBInstances();
|
||||||
if (!records.size())
|
if (!records.size())
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "No FTB instances to load.";
|
QLOG_INFO() << "No FTB instances to load.";
|
||||||
@ -363,20 +371,13 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
// process the records we acquired.
|
// process the records we acquired.
|
||||||
for (auto record : records)
|
for (auto record : records)
|
||||||
{
|
{
|
||||||
auto instanceDir = dataDir.absoluteFilePath(record.dir);
|
QLOG_INFO() << "Loading FTB instance from " << record.instanceDir;
|
||||||
QLOG_INFO() << "Loading FTB instance from " << instanceDir;
|
|
||||||
auto templateDir = dir.absoluteFilePath(record.dir);
|
|
||||||
if (!QFileInfo(instanceDir).exists())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString iconKey = record.logo;
|
QString iconKey = record.logo;
|
||||||
iconKey.remove(QRegularExpression("\\..*"));
|
iconKey.remove(QRegularExpression("\\..*"));
|
||||||
MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo),
|
MMC->icons()->addIcon(iconKey, iconKey, PathCombine(record.templateDir, record.logo),
|
||||||
MMCIcon::Transient);
|
MMCIcon::Transient);
|
||||||
|
|
||||||
if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists())
|
if (!QFileInfo(PathCombine(record.instanceDir, "instance.cfg")).exists())
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "Converting " << record.name << " as new.";
|
QLOG_INFO() << "Converting " << record.name << " as new.";
|
||||||
BaseInstance *instPtr = NULL;
|
BaseInstance *instPtr = NULL;
|
||||||
@ -384,12 +385,12 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
auto version = MMC->minecraftlist()->findVersion(record.mcVersion);
|
auto version = MMC->minecraftlist()->findVersion(record.mcVersion);
|
||||||
if (!version)
|
if (!version)
|
||||||
{
|
{
|
||||||
QLOG_ERROR() << "Can't load instance " << instanceDir
|
QLOG_ERROR() << "Can't load instance " << record.instanceDir
|
||||||
<< " because minecraft version " << record.mcVersion
|
<< " because minecraft version " << record.mcVersion
|
||||||
<< " can't be resolved.";
|
<< " can't be resolved.";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto error = factory.createInstance(instPtr, version, instanceDir,
|
auto error = factory.createInstance(instPtr, version, record.instanceDir,
|
||||||
InstanceFactory::FTBInstance);
|
InstanceFactory::FTBInstance);
|
||||||
|
|
||||||
if (!instPtr || error != InstanceFactory::NoCreateError)
|
if (!instPtr || error != InstanceFactory::NoCreateError)
|
||||||
@ -400,13 +401,15 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
instPtr->setIconKey(iconKey);
|
instPtr->setIconKey(iconKey);
|
||||||
instPtr->setIntendedVersionId(record.mcVersion);
|
instPtr->setIntendedVersionId(record.mcVersion);
|
||||||
instPtr->setNotes(record.description);
|
instPtr->setNotes(record.description);
|
||||||
continueProcessInstance(instPtr, error, instanceDir, groupMap);
|
if(!continueProcessInstance(instPtr, error, record.instanceDir, groupMap))
|
||||||
|
continue;
|
||||||
|
tempList.append(InstancePtr(instPtr));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QLOG_INFO() << "Loading existing " << record.name;
|
QLOG_INFO() << "Loading existing " << record.name;
|
||||||
BaseInstance *instPtr = NULL;
|
BaseInstance *instPtr = NULL;
|
||||||
auto error = InstanceFactory::get().loadInstance(instPtr, instanceDir);
|
auto error = InstanceFactory::get().loadInstance(instPtr, record.instanceDir);
|
||||||
if (!instPtr || error != InstanceFactory::NoCreateError)
|
if (!instPtr || error != InstanceFactory::NoCreateError)
|
||||||
continue;
|
continue;
|
||||||
instPtr->setGroupInitial("FTB");
|
instPtr->setGroupInitial("FTB");
|
||||||
@ -415,7 +418,9 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap)
|
|||||||
if (instPtr->intendedVersionId() != record.mcVersion)
|
if (instPtr->intendedVersionId() != record.mcVersion)
|
||||||
instPtr->setIntendedVersionId(record.mcVersion);
|
instPtr->setIntendedVersionId(record.mcVersion);
|
||||||
instPtr->setNotes(record.description);
|
instPtr->setNotes(record.description);
|
||||||
continueProcessInstance(instPtr, error, instanceDir, groupMap);
|
if(!continueProcessInstance(instPtr, error, record.instanceDir, groupMap))
|
||||||
|
continue;
|
||||||
|
tempList.append(InstancePtr(instPtr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,10 +431,7 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
QMap<QString, QString> groupMap;
|
QMap<QString, QString> groupMap;
|
||||||
loadGroupList(groupMap);
|
loadGroupList(groupMap);
|
||||||
|
|
||||||
beginResetModel();
|
QList<InstancePtr> tempList;
|
||||||
|
|
||||||
m_instances.clear();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
|
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable,
|
||||||
QDirIterator::FollowSymlinks);
|
QDirIterator::FollowSymlinks);
|
||||||
@ -441,15 +443,28 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
QLOG_INFO() << "Loading MultiMC instance from " << subDir;
|
QLOG_INFO() << "Loading MultiMC instance from " << subDir;
|
||||||
BaseInstance *instPtr = NULL;
|
BaseInstance *instPtr = NULL;
|
||||||
auto error = InstanceFactory::get().loadInstance(instPtr, subDir);
|
auto error = InstanceFactory::get().loadInstance(instPtr, subDir);
|
||||||
continueProcessInstance(instPtr, error, subDir, groupMap);
|
if(!continueProcessInstance(instPtr, error, subDir, groupMap))
|
||||||
|
continue;
|
||||||
|
tempList.append(InstancePtr(instPtr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MMC->settings()->get("TrackFTBInstances").toBool())
|
if (MMC->settings()->get("TrackFTBInstances").toBool())
|
||||||
{
|
{
|
||||||
loadForgeInstances(groupMap);
|
loadFTBInstances(groupMap, tempList);
|
||||||
|
}
|
||||||
|
beginResetModel();
|
||||||
|
m_instances.clear();
|
||||||
|
for(auto inst: tempList)
|
||||||
|
{
|
||||||
|
inst->setParent(this);
|
||||||
|
connect(inst.get(), SIGNAL(propertiesChanged(BaseInstance *)), this,
|
||||||
|
SLOT(propertiesChanged(BaseInstance *)));
|
||||||
|
connect(inst.get(), SIGNAL(groupChanged()), this, SLOT(groupChanged()));
|
||||||
|
connect(inst.get(), SIGNAL(nuked(BaseInstance *)), this,
|
||||||
|
SLOT(instanceNuked(BaseInstance *)));
|
||||||
|
m_instances.append(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
emit dataIsInvalid();
|
emit dataIsInvalid();
|
||||||
return NoError;
|
return NoError;
|
||||||
@ -523,7 +538,7 @@ int InstanceList::getInstIndex(BaseInstance *inst) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error,
|
bool InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error,
|
||||||
const QDir &dir, QMap<QString, QString> &groupMap)
|
const QDir &dir, QMap<QString, QString> &groupMap)
|
||||||
{
|
{
|
||||||
if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
|
if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance)
|
||||||
@ -539,12 +554,14 @@ void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int erro
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
QLOG_ERROR() << errorMsg.toUtf8();
|
QLOG_ERROR() << errorMsg.toUtf8();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else if (!instPtr)
|
else if (!instPtr)
|
||||||
{
|
{
|
||||||
QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
|
QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.")
|
||||||
.arg(QFileInfo(dir.absolutePath()).baseName())
|
.arg(QFileInfo(dir.absolutePath()).baseName())
|
||||||
.toUtf8();
|
.toUtf8();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -554,13 +571,7 @@ void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int erro
|
|||||||
instPtr->setGroupInitial((*iter));
|
instPtr->setGroupInitial((*iter));
|
||||||
}
|
}
|
||||||
QLOG_INFO() << "Loaded instance " << instPtr->name() << " from " << dir.absolutePath();
|
QLOG_INFO() << "Loaded instance " << instPtr->name() << " from " << dir.absolutePath();
|
||||||
instPtr->setParent(this);
|
return true;
|
||||||
m_instances.append(std::shared_ptr<BaseInstance>(instPtr));
|
|
||||||
connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this,
|
|
||||||
SLOT(propertiesChanged(BaseInstance *)));
|
|
||||||
connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged()));
|
|
||||||
connect(instPtr, SIGNAL(nuked(BaseInstance *)), this,
|
|
||||||
SLOT(instanceNuked(BaseInstance *)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,11 +595,8 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceProxyModel::InstanceProxyModel(QObject *parent)
|
InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(parent)
|
||||||
: KCategorizedSortFilterProxyModel(parent)
|
|
||||||
{
|
{
|
||||||
// disable since by default we are globally sorting by date:
|
|
||||||
setCategorizedModel(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
|
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include "categorizedsortfilterproxymodel.h"
|
#include <gui/groupview/GroupedProxyModel.h>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
#include "logic/BaseInstance.h"
|
#include "logic/BaseInstance.h"
|
||||||
@ -27,11 +27,24 @@ class BaseInstance;
|
|||||||
|
|
||||||
class QDir;
|
class QDir;
|
||||||
|
|
||||||
|
struct FTBRecord
|
||||||
|
{
|
||||||
|
QString dirName;
|
||||||
|
QString name;
|
||||||
|
QString logo;
|
||||||
|
QString mcVersion;
|
||||||
|
QString description;
|
||||||
|
QString instanceDir;
|
||||||
|
QString templateDir;
|
||||||
|
};
|
||||||
|
|
||||||
class InstanceList : public QAbstractListModel
|
class InstanceList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
void loadGroupList(QMap<QString, QString> &groupList);
|
void loadGroupList(QMap<QString, QString> &groupList);
|
||||||
|
QList<FTBRecord> discoverFTBInstances();
|
||||||
|
void loadFTBInstances(QMap<QString, QString> &groupMap, QList<InstancePtr> & tempList);
|
||||||
|
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
@ -109,7 +122,6 @@ slots:
|
|||||||
* \brief Loads the instance list. Triggers notifications.
|
* \brief Loads the instance list. Triggers notifications.
|
||||||
*/
|
*/
|
||||||
InstListError loadList();
|
InstListError loadList();
|
||||||
void loadForgeInstances(QMap<QString, QString> groupMap);
|
|
||||||
|
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
@ -120,7 +132,7 @@ slots:
|
|||||||
private:
|
private:
|
||||||
int getInstIndex(BaseInstance *inst) const;
|
int getInstIndex(BaseInstance *inst) const;
|
||||||
|
|
||||||
void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir,
|
bool continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir,
|
||||||
QMap<QString, QString> &groupMap);
|
QMap<QString, QString> &groupMap);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -129,7 +141,7 @@ protected:
|
|||||||
QSet<QString> m_groups;
|
QSet<QString> m_groups;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
|
class InstanceProxyModel : public GroupedProxyModel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit InstanceProxyModel(QObject *parent = 0);
|
explicit InstanceProxyModel(QObject *parent = 0);
|
||||||
|
@ -60,10 +60,15 @@ bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
|
|||||||
return left->timestamp > right->timestamp;
|
return left->timestamp > right->timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MinecraftVersionList::sortInternal()
|
||||||
|
{
|
||||||
|
qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
|
||||||
|
}
|
||||||
|
|
||||||
void MinecraftVersionList::sort()
|
void MinecraftVersionList::sort()
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
|
sortInternal();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,9 +90,8 @@ void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_vlist = versions;
|
m_vlist = versions;
|
||||||
m_loaded = true;
|
m_loaded = true;
|
||||||
|
sortInternal();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
// NOW SORT!!
|
|
||||||
sort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
|
inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
|
||||||
|
@ -29,6 +29,8 @@ class QNetworkReply;
|
|||||||
class MinecraftVersionList : public BaseVersionList
|
class MinecraftVersionList : public BaseVersionList
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
void sortInternal();
|
||||||
public:
|
public:
|
||||||
friend class MCVListLoadTask;
|
friend class MCVListLoadTask;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user