Merge branch 'master' of http://github.com/peterix/MultiMC5
Conflicts: CMakeLists.txt main.cpp
This commit is contained in:
commit
cd1fdbbbc2
@ -31,7 +31,6 @@ ENDIF()
|
|||||||
# First, include header overrides
|
# First, include header overrides
|
||||||
include_directories(hacks)
|
include_directories(hacks)
|
||||||
|
|
||||||
|
|
||||||
######## 3rd Party Libs ########
|
######## 3rd Party Libs ########
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
@ -173,6 +172,7 @@ gui/aboutdialog.h
|
|||||||
gui/consolewindow.h
|
gui/consolewindow.h
|
||||||
gui/instancemodel.h
|
gui/instancemodel.h
|
||||||
gui/instancedelegate.h
|
gui/instancedelegate.h
|
||||||
|
gui/iconcache.h
|
||||||
|
|
||||||
multimc_pragma.h
|
multimc_pragma.h
|
||||||
|
|
||||||
@ -201,6 +201,7 @@ gui/aboutdialog.cpp
|
|||||||
gui/consolewindow.cpp
|
gui/consolewindow.cpp
|
||||||
gui/instancemodel.cpp
|
gui/instancemodel.cpp
|
||||||
gui/instancedelegate.cpp
|
gui/instancedelegate.cpp
|
||||||
|
gui/iconcache.cpp
|
||||||
|
|
||||||
java/javautils.cpp
|
java/javautils.cpp
|
||||||
java/annotations.cpp
|
java/annotations.cpp
|
||||||
|
127
gui/iconcache.cpp
Normal file
127
gui/iconcache.cpp
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
#include "iconcache.h"
|
||||||
|
#include <QMap>
|
||||||
|
#include <QWebView>
|
||||||
|
#include <QWebFrame>
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QWebElement>
|
||||||
|
|
||||||
|
IconCache* IconCache::m_Instance = 0;
|
||||||
|
QMutex IconCache::mutex;
|
||||||
|
#define MAX_SIZE 1024
|
||||||
|
|
||||||
|
class Private : public QWebView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QSize size;
|
||||||
|
QMap<QString, QIcon> icons;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Private()
|
||||||
|
{
|
||||||
|
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(svgLoaded(bool)));
|
||||||
|
setFixedSize(MAX_SIZE, MAX_SIZE);
|
||||||
|
|
||||||
|
QPalette pal = palette();
|
||||||
|
pal.setColor(QPalette::Base, Qt::transparent);
|
||||||
|
setPalette(pal);
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||||
|
size = QSize(128,128);
|
||||||
|
}
|
||||||
|
void renderSVGIcon(QString name);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void svgRendered();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void svgLoaded(bool ok);
|
||||||
|
};
|
||||||
|
|
||||||
|
void Private::svgLoaded(bool ok)
|
||||||
|
{
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
emit svgRendered();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// check for SVG root tag
|
||||||
|
QString root = page()->currentFrame()->documentElement().tagName();
|
||||||
|
if (root.compare("svg", Qt::CaseInsensitive) != 0)
|
||||||
|
{
|
||||||
|
emit svgRendered();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the size of the svg image, check if it's valid
|
||||||
|
auto elem = page()->currentFrame()->documentElement();
|
||||||
|
double width = elem.attribute("width").toDouble();
|
||||||
|
double height = elem.attribute("height").toDouble();
|
||||||
|
if (width == 0.0 || height == 0.0 || width == MAX_SIZE || height == MAX_SIZE)
|
||||||
|
{
|
||||||
|
emit svgRendered();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the target surface
|
||||||
|
QSize t = size.isValid() ? size : QSize(width, height);
|
||||||
|
QImage img(t, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(Qt::transparent);
|
||||||
|
|
||||||
|
// prepare the painter, scale to required size
|
||||||
|
QPainter p(&img);
|
||||||
|
if(size.isValid())
|
||||||
|
{
|
||||||
|
p.scale(size.width() / width, size.height() / height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the best quality
|
||||||
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
|
p.setRenderHint(QPainter::TextAntialiasing);
|
||||||
|
p.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||||
|
|
||||||
|
page()->mainFrame()->render(&p,QWebFrame::ContentsLayer);
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
icons[name] = QIcon(QPixmap::fromImage(img));
|
||||||
|
emit svgRendered();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Private::renderSVGIcon ( QString name )
|
||||||
|
{
|
||||||
|
// use event loop to wait for signal
|
||||||
|
QEventLoop loop;
|
||||||
|
this->name = name;
|
||||||
|
QString prefix = "qrc:/icons/instances/";
|
||||||
|
QObject::connect(this, SIGNAL(svgRendered()), &loop, SLOT(quit()));
|
||||||
|
load(QUrl(prefix + name));
|
||||||
|
loop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
IconCache::IconCache():d(new Private())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon IconCache::getIcon ( QString name )
|
||||||
|
{
|
||||||
|
if(name == "default")
|
||||||
|
name = "infinity";
|
||||||
|
{
|
||||||
|
auto iter = d->icons.find(name);
|
||||||
|
if(iter != d->icons.end())
|
||||||
|
return *iter;
|
||||||
|
}
|
||||||
|
d->renderSVGIcon(name);
|
||||||
|
auto iter = d->icons.find(name);
|
||||||
|
if(iter != d->icons.end())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
// Fallback for icons that don't exist.
|
||||||
|
QString path = ":/icons/instances/infinity";
|
||||||
|
//path += name;
|
||||||
|
d->icons[name] = QIcon(path);
|
||||||
|
return d->icons[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "iconcache.moc"
|
43
gui/iconcache.h
Normal file
43
gui/iconcache.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
|
||||||
|
class Private;
|
||||||
|
|
||||||
|
class IconCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static IconCache* instance()
|
||||||
|
{
|
||||||
|
if (!m_Instance)
|
||||||
|
{
|
||||||
|
mutex.lock();
|
||||||
|
if (!m_Instance)
|
||||||
|
m_Instance = new IconCache;
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
return m_Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drop()
|
||||||
|
{
|
||||||
|
mutex.lock();
|
||||||
|
delete m_Instance;
|
||||||
|
m_Instance = 0;
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon getIcon(QString name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IconCache();
|
||||||
|
// hide copy constructor
|
||||||
|
IconCache(const IconCache &);
|
||||||
|
// hide assign op
|
||||||
|
IconCache& operator=(const IconCache &);
|
||||||
|
static IconCache* m_Instance;
|
||||||
|
static QMutex mutex;
|
||||||
|
Private* d;
|
||||||
|
};
|
||||||
|
|
@ -33,9 +33,15 @@ ListViewDelegate::ListViewDelegate ( QObject* parent ) : QStyledItemDelegate ( p
|
|||||||
|
|
||||||
void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
|
void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
|
||||||
{
|
{
|
||||||
if (!(option.state & QStyle::State_Selected))
|
if ((option.state & QStyle::State_Selected))
|
||||||
return;
|
painter->fillRect ( rect, option.palette.brush ( QPalette::Highlight ) );
|
||||||
painter->fillRect ( rect, option.palette.brush ( QPalette::Highlight ) );
|
else
|
||||||
|
{
|
||||||
|
QColor backgroundColor = option.palette.color(QPalette::Background);
|
||||||
|
backgroundColor.setAlpha(160);
|
||||||
|
painter->fillRect ( rect, QBrush(backgroundColor) );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
|
void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
|
||||||
|
@ -1,13 +1,38 @@
|
|||||||
#include "instancemodel.h"
|
#include "instancemodel.h"
|
||||||
#include <instance.h>
|
#include <instance.h>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include "iconcache.h"
|
||||||
|
|
||||||
InstanceModel::InstanceModel ( const InstanceList& instances, QObject *parent )
|
InstanceModel::InstanceModel ( const InstanceList& instances, QObject *parent )
|
||||||
: QAbstractListModel ( parent ), m_instances ( &instances )
|
: QAbstractListModel ( parent ), m_instances ( &instances )
|
||||||
{
|
{
|
||||||
cachedIcon = QIcon(":/icons/multimc/scalable/apps/multimc.svg");
|
currentInstancesNumber = m_instances->count();
|
||||||
|
connect(m_instances,SIGNAL(instanceAdded(int)),this,SLOT(onInstanceAdded(int)));
|
||||||
|
connect(m_instances,SIGNAL(instanceChanged(int)),this,SLOT(onInstanceChanged(int)));
|
||||||
|
connect(m_instances,SIGNAL(invalidated()),this,SLOT(onInvalidated()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceModel::onInstanceAdded ( int index )
|
||||||
|
{
|
||||||
|
beginInsertRows(QModelIndex(), index, index);
|
||||||
|
currentInstancesNumber ++;
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceModel::onInstanceChanged ( int index )
|
||||||
|
{
|
||||||
|
QModelIndex mx = InstanceModel::index(index);
|
||||||
|
dataChanged(mx,mx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceModel::onInvalidated()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
currentInstancesNumber = m_instances->count();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int InstanceModel::rowCount ( const QModelIndex& parent ) const
|
int InstanceModel::rowCount ( const QModelIndex& parent ) const
|
||||||
{
|
{
|
||||||
Q_UNUSED ( parent );
|
Q_UNUSED ( parent );
|
||||||
@ -17,7 +42,7 @@ int InstanceModel::rowCount ( const QModelIndex& parent ) const
|
|||||||
QModelIndex InstanceModel::index ( int row, int column, const QModelIndex& parent ) const
|
QModelIndex InstanceModel::index ( int row, int column, const QModelIndex& parent ) const
|
||||||
{
|
{
|
||||||
Q_UNUSED ( parent );
|
Q_UNUSED ( parent );
|
||||||
if ( row < 0 || row >= m_instances->count() )
|
if ( row < 0 || row >= currentInstancesNumber )
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
return createIndex ( row, column, ( void* ) m_instances->at ( row ).data() );
|
return createIndex ( row, column, ( void* ) m_instances->at ( row ).data() );
|
||||||
}
|
}
|
||||||
@ -46,14 +71,22 @@ QVariant InstanceModel::data ( const QModelIndex& index, int role ) const
|
|||||||
}
|
}
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
{
|
{
|
||||||
// FIXME: replace with an icon cache
|
IconCache * ic = IconCache::instance();
|
||||||
return cachedIcon;
|
// FIXME: replace with an icon cache/renderer
|
||||||
|
/*
|
||||||
|
QString path = ":/icons/instances/";
|
||||||
|
path += pdata->iconKey();
|
||||||
|
QIcon icon(path);
|
||||||
|
*/
|
||||||
|
QString key = pdata->iconKey();
|
||||||
|
return ic->getIcon(key);
|
||||||
|
//else return QIcon(":/icons/multimc/scalable/apps/multimc.svg");
|
||||||
}
|
}
|
||||||
// for now.
|
// for now.
|
||||||
case KCategorizedSortFilterProxyModel::CategorySortRole:
|
case KCategorizedSortFilterProxyModel::CategorySortRole:
|
||||||
case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
|
case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
|
||||||
{
|
{
|
||||||
return "IT'S A GROUP";
|
return pdata->group();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -22,9 +22,14 @@ public:
|
|||||||
QVariant data ( const QModelIndex& index, int role ) const;
|
QVariant data ( const QModelIndex& index, int role ) const;
|
||||||
Qt::ItemFlags flags ( const QModelIndex& index ) const;
|
Qt::ItemFlags flags ( const QModelIndex& index ) const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onInstanceAdded(int index);
|
||||||
|
void onInstanceChanged(int index);
|
||||||
|
void onInvalidated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const InstanceList* m_instances;
|
const InstanceList* m_instances;
|
||||||
QIcon cachedIcon;
|
int currentInstancesNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
|
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
|
||||||
|
@ -64,10 +64,24 @@ MainWindow::MainWindow ( QWidget *parent ) :
|
|||||||
{
|
{
|
||||||
ui->setupUi ( this );
|
ui->setupUi ( this );
|
||||||
// Create the widget
|
// Create the widget
|
||||||
instList.loadList();
|
|
||||||
|
|
||||||
view = new KCategorizedView ( ui->centralWidget );
|
view = new KCategorizedView ( ui->centralWidget );
|
||||||
drawer = new KCategoryDrawer ( view );
|
drawer = new KCategoryDrawer ( view );
|
||||||
|
/*
|
||||||
|
QPalette pal = view->palette();
|
||||||
|
pal.setBrush(QPalette::Base, QBrush(QPixmap(QString::fromUtf8(":/backgrounds/kitteh"))));
|
||||||
|
view->setPalette(pal);
|
||||||
|
*/
|
||||||
|
|
||||||
|
view->setStyleSheet(
|
||||||
|
"QListView\
|
||||||
|
{\
|
||||||
|
background-image: url(:/backgrounds/kitteh);\
|
||||||
|
background-attachment: fixed;\
|
||||||
|
background-clip: padding;\
|
||||||
|
background-position: top right;\
|
||||||
|
background-repeat: none;\
|
||||||
|
background-color:palette(base);\
|
||||||
|
}");
|
||||||
|
|
||||||
view->setSelectionMode ( QAbstractItemView::SingleSelection );
|
view->setSelectionMode ( QAbstractItemView::SingleSelection );
|
||||||
//view->setSpacing( KDialog::spacingHint() );
|
//view->setSpacing( KDialog::spacingHint() );
|
||||||
@ -101,6 +115,13 @@ MainWindow::MainWindow ( QWidget *parent ) :
|
|||||||
connect(view, SIGNAL(doubleClicked(const QModelIndex &)),
|
connect(view, SIGNAL(doubleClicked(const QModelIndex &)),
|
||||||
this, SLOT(instanceActivated(const QModelIndex &)));
|
this, SLOT(instanceActivated(const QModelIndex &)));
|
||||||
|
|
||||||
|
// Load the instances.
|
||||||
|
instList.loadList();
|
||||||
|
// just a test
|
||||||
|
/*
|
||||||
|
instList.at(0)->setGroup("TEST GROUP");
|
||||||
|
instList.at(0)->setName("TEST ITEM");
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
@ -125,6 +146,18 @@ void MainWindow::on_actionAddInstance_triggered()
|
|||||||
newInstDlg->exec();
|
newInstDlg->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionChangeInstGroup_triggered()
|
||||||
|
{
|
||||||
|
Instance* inst = selectedInstance();
|
||||||
|
if(inst)
|
||||||
|
{
|
||||||
|
QString name ( inst->group() );
|
||||||
|
name = QInputDialog::getText ( this, tr ( "Group name" ), tr ( "Enter a new group name." ), QLineEdit::Normal, name );
|
||||||
|
inst->setGroup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_actionViewInstanceFolder_triggered()
|
void MainWindow::on_actionViewInstanceFolder_triggered()
|
||||||
{
|
{
|
||||||
openInDefaultProgram ( globalSettings->get ( "InstanceDir" ).toString() );
|
openInDefaultProgram ( globalSettings->get ( "InstanceDir" ).toString() );
|
||||||
@ -195,13 +228,31 @@ void MainWindow::on_instanceView_customContextMenuRequested ( const QPoint &pos
|
|||||||
instContextMenu->exec ( view->mapToGlobal ( pos ) );
|
instContextMenu->exec ( view->mapToGlobal ( pos ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Instance* MainWindow::selectedInstance()
|
||||||
|
{
|
||||||
|
QAbstractItemView * iv = view;
|
||||||
|
auto smodel = iv->selectionModel();
|
||||||
|
QModelIndex mindex;
|
||||||
|
if(smodel->hasSelection())
|
||||||
|
{
|
||||||
|
auto rows = smodel->selectedRows();
|
||||||
|
mindex = rows.at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mindex.isValid())
|
||||||
|
{
|
||||||
|
return (Instance *) mindex.data(InstanceModel::InstancePointerRole).value<void *>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::on_actionLaunchInstance_triggered()
|
void MainWindow::on_actionLaunchInstance_triggered()
|
||||||
{
|
{
|
||||||
QModelIndex index = view->currentIndex();
|
Instance* inst = selectedInstance();
|
||||||
if(index.isValid())
|
if(inst)
|
||||||
{
|
{
|
||||||
Instance * inst = (Instance *) index.data(InstanceModel::InstancePointerRole).value<void *>();
|
|
||||||
doLogin(inst->id());
|
doLogin(inst->id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,16 @@ public:
|
|||||||
// Browser Dialog
|
// Browser Dialog
|
||||||
void openWebPage(QUrl url);
|
void openWebPage(QUrl url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Instance *selectedInstance();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_actionAbout_triggered();
|
void on_actionAbout_triggered();
|
||||||
|
|
||||||
void on_actionAddInstance_triggered();
|
void on_actionAddInstance_triggered();
|
||||||
|
|
||||||
|
void on_actionChangeInstGroup_triggered();
|
||||||
|
|
||||||
void on_actionViewInstanceFolder_triggered();
|
void on_actionViewInstanceFolder_triggered();
|
||||||
|
|
||||||
void on_actionRefresh_triggered();
|
void on_actionRefresh_triggered();
|
||||||
|
@ -65,6 +65,9 @@ class LIBMULTIMC_EXPORT Instance : public QObject
|
|||||||
//! The instance's notes.
|
//! The instance's notes.
|
||||||
Q_PROPERTY(QString notes READ notes WRITE setNotes)
|
Q_PROPERTY(QString notes READ notes WRITE setNotes)
|
||||||
|
|
||||||
|
//! The instance's group.
|
||||||
|
Q_PROPERTY(QString group READ group WRITE setGroup)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Whether or not the instance's minecraft.jar needs to be rebuilt.
|
* Whether or not the instance's minecraft.jar needs to be rebuilt.
|
||||||
* If this is true, when the instance launches, its jar mods will be
|
* If this is true, when the instance launches, its jar mods will be
|
||||||
@ -173,14 +176,29 @@ public:
|
|||||||
|
|
||||||
//// General Info ////
|
//// General Info ////
|
||||||
virtual QString name() { return settings().get("name").toString(); }
|
virtual QString name() { return settings().get("name").toString(); }
|
||||||
virtual void setName(QString val) { settings().set("name", val); }
|
virtual void setName(QString val)
|
||||||
|
{
|
||||||
|
settings().set("name", val);
|
||||||
|
emit propertiesChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
virtual QString iconKey() const { return settings().get("iconKey").toString(); }
|
virtual QString iconKey() const { return settings().get("iconKey").toString(); }
|
||||||
virtual void setIconKey(QString val) { settings().set("iconKey", val); }
|
virtual void setIconKey(QString val)
|
||||||
|
{
|
||||||
|
settings().set("iconKey", val);
|
||||||
|
emit propertiesChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
virtual QString notes() const { return settings().get("notes").toString(); }
|
virtual QString notes() const { return settings().get("notes").toString(); }
|
||||||
virtual void setNotes(QString val) { settings().set("notes", val); }
|
virtual void setNotes(QString val) { settings().set("notes", val); }
|
||||||
|
|
||||||
|
virtual QString group() const { return m_group; }
|
||||||
|
virtual void setGroup(QString val)
|
||||||
|
{
|
||||||
|
m_group = val;
|
||||||
|
emit propertiesChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool shouldRebuild() const { return settings().get("NeedsRebuild").toBool(); }
|
virtual bool shouldRebuild() const { return settings().get("NeedsRebuild").toBool(); }
|
||||||
virtual void setShouldRebuild(bool val) { settings().set("NeedsRebuild", val); }
|
virtual void setShouldRebuild(bool val) { settings().set("NeedsRebuild", val); }
|
||||||
|
|
||||||
@ -202,7 +220,10 @@ public:
|
|||||||
|
|
||||||
virtual qint64 lastLaunch() { return settings().get("lastLaunchTime").value<qint64>(); }
|
virtual qint64 lastLaunch() { return settings().get("lastLaunchTime").value<qint64>(); }
|
||||||
virtual void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch())
|
virtual void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch())
|
||||||
{ settings().set("lastLaunchTime", val); }
|
{
|
||||||
|
settings().set("lastLaunchTime", val);
|
||||||
|
emit propertiesChanged(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////// Directories //////
|
////// Directories //////
|
||||||
@ -277,8 +298,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual SettingsObject &settings() const;
|
virtual SettingsObject &settings() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/*!
|
||||||
|
* \brief Signal emitted when properties relevant to the instance view change
|
||||||
|
*/
|
||||||
|
void propertiesChanged(Instance * inst);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_rootDir;
|
QString m_rootDir;
|
||||||
|
QString m_group;
|
||||||
SettingsObject *m_settings;
|
SettingsObject *m_settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,16 +17,14 @@
|
|||||||
#define INSTANCELIST_H
|
#define INSTANCELIST_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "siglist.h"
|
#include "instance.h"
|
||||||
|
|
||||||
#include "libmmc_config.h"
|
#include "libmmc_config.h"
|
||||||
|
|
||||||
class Instance;
|
class Instance;
|
||||||
|
|
||||||
class LIBMULTIMC_EXPORT InstanceList : public QObject, public SigList< QSharedPointer<Instance> >
|
class LIBMULTIMC_EXPORT InstanceList : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@ -46,14 +44,46 @@ public:
|
|||||||
QString instDir() const { return m_instDir; }
|
QString instDir() const { return m_instDir; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Loads the instance list.
|
* \brief Loads the instance list. Triggers notifications.
|
||||||
*/
|
*/
|
||||||
InstListError loadList();
|
InstListError loadList();
|
||||||
|
|
||||||
DEFINE_SIGLIST_SIGNALS(QSharedPointer<Instance>);
|
/*!
|
||||||
SETUP_SIGLIST_SIGNALS(QSharedPointer<Instance>);
|
* \brief Get the instance at index
|
||||||
|
*/
|
||||||
|
InstancePtr at(int i) const
|
||||||
|
{
|
||||||
|
return m_instances.at(i);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Get the count of loaded instances
|
||||||
|
*/
|
||||||
|
int count() const
|
||||||
|
{
|
||||||
|
return m_instances.count();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Clear all instances. Triggers notifications.
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/// Add an instance. Triggers notifications, returns the new index
|
||||||
|
int add(InstancePtr t);
|
||||||
|
|
||||||
|
/// Get an instance by ID
|
||||||
|
InstancePtr getInstanceById (QString id);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void instanceAdded(int index);
|
||||||
|
void instanceChanged(int index);
|
||||||
|
void invalidated();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void propertiesChanged(Instance * inst);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_instDir;
|
QString m_instDir;
|
||||||
|
QList< InstancePtr > m_instances;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INSTANCELIST_H
|
#endif // INSTANCELIST_H
|
||||||
|
@ -15,17 +15,21 @@
|
|||||||
|
|
||||||
#include "include/instancelist.h"
|
#include "include/instancelist.h"
|
||||||
|
|
||||||
#include "siglist_impl.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
|
||||||
#include "include/instance.h"
|
#include "include/instance.h"
|
||||||
#include "include/instanceloader.h"
|
#include "include/instanceloader.h"
|
||||||
|
|
||||||
#include "pathutils.h"
|
#include "pathutils.h"
|
||||||
|
|
||||||
|
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||||
|
|
||||||
InstanceList::InstanceList(const QString &instDir, QObject *parent) :
|
InstanceList::InstanceList(const QString &instDir, QObject *parent) :
|
||||||
QObject(parent), m_instDir("instances")
|
QObject(parent), m_instDir("instances")
|
||||||
@ -38,6 +42,104 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
QDir dir(m_instDir);
|
QDir dir(m_instDir);
|
||||||
QDirIterator iter(dir);
|
QDirIterator iter(dir);
|
||||||
|
|
||||||
|
QString groupFileName = m_instDir + "/instgroups.json";
|
||||||
|
// temporary map from instance ID to group name
|
||||||
|
QMap<QString, QString> groupMap;
|
||||||
|
|
||||||
|
// HACK: this is really an if. breaks after one iteration.
|
||||||
|
while (QFileInfo(groupFileName).exists())
|
||||||
|
{
|
||||||
|
QFile groupFile(groupFileName);
|
||||||
|
|
||||||
|
if (!groupFile.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
// An error occurred. Ignore it.
|
||||||
|
qDebug("Failed to read instance group file.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in(&groupFile);
|
||||||
|
QString jsonStr = in.readAll();
|
||||||
|
groupFile.close();
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
|
||||||
|
|
||||||
|
if (error.error != QJsonParseError::NoError)
|
||||||
|
{
|
||||||
|
qWarning(QString("Failed to parse instance group file: %1 at offset %2").
|
||||||
|
arg(error.errorString(), QString::number(error.offset)).toUtf8());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jsonDoc.isObject())
|
||||||
|
{
|
||||||
|
qWarning("Invalid group file. Root entry should be an object.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject rootObj = jsonDoc.object();
|
||||||
|
|
||||||
|
// Make sure the format version matches.
|
||||||
|
if (rootObj.value("formatVersion").toVariant().toInt() == GROUP_FILE_FORMAT_VERSION)
|
||||||
|
{
|
||||||
|
// Get the group list.
|
||||||
|
if (!rootObj.value("groups").isObject())
|
||||||
|
{
|
||||||
|
qWarning("Invalid group list JSON: 'groups' should be an object.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the list.
|
||||||
|
QJsonObject groupList = rootObj.value("groups").toObject();
|
||||||
|
|
||||||
|
for (QJsonObject::iterator iter = groupList.begin();
|
||||||
|
iter != groupList.end(); iter++)
|
||||||
|
{
|
||||||
|
QString groupName = iter.key();
|
||||||
|
|
||||||
|
// If not an object, complain and skip to the next one.
|
||||||
|
if (!iter.value().isObject())
|
||||||
|
{
|
||||||
|
qWarning(QString("Group '%1' in the group list should "
|
||||||
|
"be an object.").arg(groupName).toUtf8());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject groupObj = iter.value().toObject();
|
||||||
|
/*
|
||||||
|
// Create the group object.
|
||||||
|
InstanceGroup *group = new InstanceGroup(groupName, this);
|
||||||
|
groups.push_back(group);
|
||||||
|
|
||||||
|
// If 'hidden' isn't a bool value, just assume it's false.
|
||||||
|
if (groupObj.value("hidden").isBool() && groupObj.value("hidden").toBool())
|
||||||
|
{
|
||||||
|
group->setHidden(groupObj.value("hidden").toBool());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!groupObj.value("instances").isArray())
|
||||||
|
{
|
||||||
|
qWarning(QString("Group '%1' in the group list is invalid. "
|
||||||
|
"It should contain an array "
|
||||||
|
"called 'instances'.").arg(groupName).toUtf8());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the list of instances in the group.
|
||||||
|
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||||
|
|
||||||
|
for (QJsonArray::iterator iter2 = instancesArray.begin();
|
||||||
|
iter2 != instancesArray.end(); iter2++)
|
||||||
|
{
|
||||||
|
groupMap[(*iter2).toString()] = groupName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_instances.clear();
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
QString subDir = iter.next();
|
QString subDir = iter.next();
|
||||||
@ -75,13 +177,61 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
QSharedPointer<Instance> inst(instPtr);
|
QSharedPointer<Instance> inst(instPtr);
|
||||||
|
auto iter = groupMap.find(inst->id());
|
||||||
|
if(iter != groupMap.end())
|
||||||
|
{
|
||||||
|
inst->setGroup((*iter));
|
||||||
|
}
|
||||||
qDebug(QString("Loaded instance %1").arg(inst->name()).toUtf8());
|
qDebug(QString("Loaded instance %1").arg(inst->name()).toUtf8());
|
||||||
inst->setParent(this);
|
inst->setParent(this);
|
||||||
append(QSharedPointer<Instance>(inst));
|
m_instances.append(inst);
|
||||||
|
connect(instPtr, SIGNAL(propertiesChanged(Instance*)),this, SLOT(propertiesChanged(Instance*)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit invalidated();
|
||||||
return NoError;
|
return NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear all instances. Triggers notifications.
|
||||||
|
void InstanceList::clear()
|
||||||
|
{
|
||||||
|
m_instances.clear();
|
||||||
|
emit invalidated();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Add an instance. Triggers notifications, returns the new index
|
||||||
|
int InstanceList::add(InstancePtr t)
|
||||||
|
{
|
||||||
|
m_instances.append(t);
|
||||||
|
emit instanceAdded(count() - 1);
|
||||||
|
return count() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstancePtr InstanceList::getInstanceById(QString instId)
|
||||||
|
{
|
||||||
|
QListIterator<InstancePtr> iter(m_instances);
|
||||||
|
InstancePtr inst;
|
||||||
|
while(iter.hasNext())
|
||||||
|
{
|
||||||
|
inst = iter.next();
|
||||||
|
if (inst->id() == instId)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (inst->id() != instId)
|
||||||
|
return InstancePtr();
|
||||||
|
else
|
||||||
|
return iter.peekPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceList::propertiesChanged(Instance * inst)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < m_instances.count(); i++)
|
||||||
|
{
|
||||||
|
if(inst == m_instances[i].data())
|
||||||
|
{
|
||||||
|
emit instanceChanged(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,9 +31,6 @@ include/pathutils.h
|
|||||||
include/osutils.h
|
include/osutils.h
|
||||||
include/userutils.h
|
include/userutils.h
|
||||||
include/cmdutils.h
|
include/cmdutils.h
|
||||||
|
|
||||||
include/siglist.h
|
|
||||||
include/siglist_impl.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(LIBUTIL_SOURCES
|
SET(LIBUTIL_SOURCES
|
||||||
|
16
main.cpp
16
main.cpp
@ -57,20 +57,6 @@ public:
|
|||||||
this->instId = instId;
|
this->instId = instId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
InstancePtr findInstance(QString instId)
|
|
||||||
{
|
|
||||||
QListIterator<InstancePtr> iter(instances);
|
|
||||||
InstancePtr inst;
|
|
||||||
while(iter.hasNext())
|
|
||||||
{
|
|
||||||
inst = iter.next();
|
|
||||||
if (inst->id() == instId)
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
return InstancePtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onTerminated()
|
void onTerminated()
|
||||||
{
|
{
|
||||||
@ -114,7 +100,7 @@ public:
|
|||||||
instances.loadList();
|
instances.loadList();
|
||||||
|
|
||||||
std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
|
std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
|
||||||
instance = findInstance(instId);
|
instance = instances.getInstanceById(instId);
|
||||||
if (instance.isNull())
|
if (instance.isNull())
|
||||||
{
|
{
|
||||||
std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;
|
std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;
|
||||||
|
@ -34,4 +34,7 @@
|
|||||||
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
|
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
|
||||||
<file alias="index.theme">resources/XdgIcon.theme</file>
|
<file alias="index.theme">resources/XdgIcon.theme</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
<qresource prefix="/backgrounds">
|
||||||
|
<file alias="kitteh">resources/catbgrnd2.png</file>
|
||||||
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
BIN
resources/catbgrnd2.png
Normal file
BIN
resources/catbgrnd2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
@ -13,26 +13,13 @@
|
|||||||
height="32"
|
height="32"
|
||||||
id="svg2"
|
id="svg2"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.48.3.1 r9886"
|
inkscape:version="0.48.4 r9939"
|
||||||
sodipodi:docname="skeleton.svg"
|
sodipodi:docname="skeleton.svg"
|
||||||
inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/skeleton128.png"
|
inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/skeleton128.png"
|
||||||
inkscape:export-xdpi="360"
|
inkscape:export-xdpi="360"
|
||||||
inkscape:export-ydpi="360">
|
inkscape:export-ydpi="360">
|
||||||
<defs
|
<defs
|
||||||
id="defs4">
|
id="defs4">
|
||||||
<filter
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="filter5719"
|
|
||||||
x="-0.18000001"
|
|
||||||
width="1.36"
|
|
||||||
y="-0.36000001"
|
|
||||||
height="1.72">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="0.15"
|
|
||||||
id="feGaussianBlur5721" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
<filter
|
||||||
color-interpolation-filters="sRGB"
|
color-interpolation-filters="sRGB"
|
||||||
inkscape:collect="always"
|
inkscape:collect="always"
|
||||||
@ -46,19 +33,6 @@
|
|||||||
stdDeviation="0.35"
|
stdDeviation="0.35"
|
||||||
id="feGaussianBlur5725" />
|
id="feGaussianBlur5725" />
|
||||||
</filter>
|
</filter>
|
||||||
<filter
|
|
||||||
color-interpolation-filters="sRGB"
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="filter5711"
|
|
||||||
x="-0.1728"
|
|
||||||
width="1.3456"
|
|
||||||
y="-0.34560001"
|
|
||||||
height="1.6912">
|
|
||||||
<feGaussianBlur
|
|
||||||
inkscape:collect="always"
|
|
||||||
stdDeviation="0.144"
|
|
||||||
id="feGaussianBlur5713" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
<filter
|
||||||
color-interpolation-filters="sRGB"
|
color-interpolation-filters="sRGB"
|
||||||
inkscape:collect="always"
|
inkscape:collect="always"
|
||||||
@ -103,12 +77,12 @@
|
|||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="11.313708"
|
inkscape:zoom="11.313708"
|
||||||
inkscape:cx="17.044214"
|
inkscape:cx="-18.309169"
|
||||||
inkscape:cy="19.500236"
|
inkscape:cy="22.958832"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
inkscape:window-width="1607"
|
inkscape:window-width="1614"
|
||||||
inkscape:window-height="1030"
|
inkscape:window-height="1030"
|
||||||
inkscape:window-x="1676"
|
inkscape:window-x="1676"
|
||||||
inkscape:window-y="-3"
|
inkscape:window-y="-3"
|
||||||
@ -129,7 +103,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@ -612,7 +586,7 @@
|
|||||||
x="7.9999995"
|
x="7.9999995"
|
||||||
y="1036.3622" />
|
y="1036.3622" />
|
||||||
<rect
|
<rect
|
||||||
style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5711)"
|
style="fill:#00ffff;fill-opacity:1;stroke:none;"
|
||||||
id="rect4084-3"
|
id="rect4084-3"
|
||||||
width="2"
|
width="2"
|
||||||
height="1"
|
height="1"
|
||||||
@ -626,7 +600,7 @@
|
|||||||
x="20"
|
x="20"
|
||||||
y="1036.3622" />
|
y="1036.3622" />
|
||||||
<rect
|
<rect
|
||||||
style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5719)"
|
style="fill:#00ffff;fill-opacity:1;stroke:none;"
|
||||||
id="rect4086-7"
|
id="rect4086-7"
|
||||||
width="2"
|
width="2"
|
||||||
height="1"
|
height="1"
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue
Block a user