Conflicts:
	CMakeLists.txt
	main.cpp
This commit is contained in:
Orochimarufan 2013-03-22 14:01:54 +01:00
commit cd1fdbbbc2
16 changed files with 523 additions and 84 deletions

View File

@ -31,7 +31,6 @@ ENDIF()
# First, include header overrides
include_directories(hacks)
######## 3rd Party Libs ########
# Find the required Qt parts
@ -173,6 +172,7 @@ gui/aboutdialog.h
gui/consolewindow.h
gui/instancemodel.h
gui/instancedelegate.h
gui/iconcache.h
multimc_pragma.h
@ -201,6 +201,7 @@ gui/aboutdialog.cpp
gui/consolewindow.cpp
gui/instancemodel.cpp
gui/instancedelegate.cpp
gui/iconcache.cpp
java/javautils.cpp
java/annotations.cpp

127
gui/iconcache.cpp Normal file
View 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
View 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;
};

View File

@ -33,9 +33,15 @@ ListViewDelegate::ListViewDelegate ( QObject* parent ) : QStyledItemDelegate ( p
void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
{
if (!(option.state & QStyle::State_Selected))
return;
painter->fillRect ( rect, option.palette.brush ( QPalette::Highlight ) );
if ((option.state & QStyle::State_Selected))
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)

View File

@ -1,13 +1,38 @@
#include "instancemodel.h"
#include <instance.h>
#include <QIcon>
#include "iconcache.h"
InstanceModel::InstanceModel ( const InstanceList& instances, QObject *parent )
: 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
{
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
{
Q_UNUSED ( parent );
if ( row < 0 || row >= m_instances->count() )
if ( row < 0 || row >= currentInstancesNumber )
return QModelIndex();
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:
{
// FIXME: replace with an icon cache
return cachedIcon;
IconCache * ic = IconCache::instance();
// 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.
case KCategorizedSortFilterProxyModel::CategorySortRole:
case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
{
return "IT'S A GROUP";
return pdata->group();
}
default:
break;

View File

@ -22,9 +22,14 @@ public:
QVariant data ( const QModelIndex& index, int role ) const;
Qt::ItemFlags flags ( const QModelIndex& index ) const;
public slots:
void onInstanceAdded(int index);
void onInstanceChanged(int index);
void onInvalidated();
private:
const InstanceList* m_instances;
QIcon cachedIcon;
int currentInstancesNumber;
};
class InstanceProxyModel : public KCategorizedSortFilterProxyModel

View File

@ -64,11 +64,25 @@ MainWindow::MainWindow ( QWidget *parent ) :
{
ui->setupUi ( this );
// Create the widget
instList.loadList();
view = new KCategorizedView ( ui->centralWidget );
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->setSpacing( KDialog::spacingHint() );
view->setCategoryDrawer ( drawer );
@ -100,7 +114,14 @@ MainWindow::MainWindow ( QWidget *parent ) :
view->setModel ( proxymodel );
connect(view, SIGNAL(doubleClicked(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()
@ -125,6 +146,18 @@ void MainWindow::on_actionAddInstance_triggered()
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()
{
openInDefaultProgram ( globalSettings->get ( "InstanceDir" ).toString() );
@ -195,13 +228,31 @@ void MainWindow::on_instanceView_customContextMenuRequested ( const QPoint &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()
{
QModelIndex index = view->currentIndex();
if(index.isValid())
Instance* inst = selectedInstance();
if(inst)
{
Instance * inst = (Instance *) index.data(InstanceModel::InstancePointerRole).value<void *>();
doLogin(inst->id());
}
}

View File

@ -44,14 +44,19 @@ public:
// Browser Dialog
void openWebPage(QUrl url);
private:
Instance *selectedInstance();
private slots:
void on_actionAbout_triggered();
void on_actionAddInstance_triggered();
void on_actionViewInstanceFolder_triggered();
void on_actionChangeInstGroup_triggered();
void on_actionViewInstanceFolder_triggered();
void on_actionRefresh_triggered();
void on_actionViewCentralModsFolder_triggered();

View File

@ -65,6 +65,9 @@ class LIBMULTIMC_EXPORT Instance : public QObject
//! The instance's notes.
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.
* If this is true, when the instance launches, its jar mods will be
@ -173,14 +176,29 @@ public:
//// General Info ////
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 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 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 void setShouldRebuild(bool val) { settings().set("NeedsRebuild", val); }
@ -202,7 +220,10 @@ public:
virtual qint64 lastLaunch() { return settings().get("lastLaunchTime").value<qint64>(); }
virtual void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch())
{ settings().set("lastLaunchTime", val); }
{
settings().set("lastLaunchTime", val);
emit propertiesChanged(this);
}
////// Directories //////
@ -277,8 +298,15 @@ public:
*/
virtual SettingsObject &settings() const;
signals:
/*!
* \brief Signal emitted when properties relevant to the instance view change
*/
void propertiesChanged(Instance * inst);
private:
QString m_rootDir;
QString m_group;
SettingsObject *m_settings;
};

View File

@ -17,16 +17,14 @@
#define INSTANCELIST_H
#include <QObject>
#include <QSharedPointer>
#include "siglist.h"
#include "instance.h"
#include "libmmc_config.h"
class Instance;
class LIBMULTIMC_EXPORT InstanceList : public QObject, public SigList< QSharedPointer<Instance> >
class LIBMULTIMC_EXPORT InstanceList : public QObject
{
Q_OBJECT
public:
@ -46,14 +44,46 @@ public:
QString instDir() const { return m_instDir; }
/*!
* \brief Loads the instance list.
* \brief Loads the instance list. Triggers notifications.
*/
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:
QString m_instDir;
QList< InstancePtr > m_instances;
};
#endif // INSTANCELIST_H

View File

@ -15,17 +15,21 @@
#include "include/instancelist.h"
#include "siglist_impl.h"
#include <QDir>
#include <QFile>
#include <QDirIterator>
#include <QThread>
#include <QTextStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include "include/instance.h"
#include "include/instanceloader.h"
#include "pathutils.h"
const static int GROUP_FILE_FORMAT_VERSION = 1;
InstanceList::InstanceList(const QString &instDir, QObject *parent) :
QObject(parent), m_instDir("instances")
@ -38,6 +42,104 @@ InstanceList::InstListError InstanceList::loadList()
QDir dir(m_instDir);
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())
{
QString subDir = iter.next();
@ -75,13 +177,61 @@ InstanceList::InstListError InstanceList::loadList()
else
{
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());
inst->setParent(this);
append(QSharedPointer<Instance>(inst));
m_instances.append(inst);
connect(instPtr, SIGNAL(propertiesChanged(Instance*)),this, SLOT(propertiesChanged(Instance*)));
}
}
}
emit invalidated();
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;
}
}
}

View File

@ -31,9 +31,6 @@ include/pathutils.h
include/osutils.h
include/userutils.h
include/cmdutils.h
include/siglist.h
include/siglist_impl.h
)
SET(LIBUTIL_SOURCES

View File

@ -57,20 +57,6 @@ public:
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:
void onTerminated()
{
@ -114,7 +100,7 @@ public:
instances.loadList();
std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
instance = findInstance(instId);
instance = instances.getInstanceById(instId);
if (instance.isNull())
{
std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;

View File

@ -34,4 +34,7 @@
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
<file alias="index.theme">resources/XdgIcon.theme</file>
</qresource>
<qresource prefix="/backgrounds">
<file alias="kitteh">resources/catbgrnd2.png</file>
</qresource>
</RCC>

BIN
resources/catbgrnd2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -13,26 +13,13 @@
height="32"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
inkscape:version="0.48.4 r9939"
sodipodi:docname="skeleton.svg"
inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/skeleton128.png"
inkscape:export-xdpi="360"
inkscape:export-ydpi="360">
<defs
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
color-interpolation-filters="sRGB"
inkscape:collect="always"
@ -46,19 +33,6 @@
stdDeviation="0.35"
id="feGaussianBlur5725" />
</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
color-interpolation-filters="sRGB"
inkscape:collect="always"
@ -103,12 +77,12 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313708"
inkscape:cx="17.044214"
inkscape:cy="19.500236"
inkscape:cx="-18.309169"
inkscape:cy="22.958832"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1607"
inkscape:window-width="1614"
inkscape:window-height="1030"
inkscape:window-x="1676"
inkscape:window-y="-3"
@ -129,7 +103,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -612,7 +586,7 @@
x="7.9999995"
y="1036.3622" />
<rect
style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5711)"
style="fill:#00ffff;fill-opacity:1;stroke:none;"
id="rect4084-3"
width="2"
height="1"
@ -626,7 +600,7 @@
x="20"
y="1036.3622" />
<rect
style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5719)"
style="fill:#00ffff;fill-opacity:1;stroke:none;"
id="rect4086-7"
width="2"
height="1"

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB