Merge branch 'master' of git://github.com/peterix/MultiMC5

Conflicts:
	CMakeLists.txt
	main.cpp
This commit is contained in:
Andrew 2013-02-25 13:31:37 -06:00
commit 23474da175
25 changed files with 2196 additions and 22 deletions

View File

@ -123,6 +123,7 @@ IF(DEFINED MultiMC_BUILD_TAG)
MESSAGE(STATUS "Build tag: ${MultiMC_BUILD_TAG}")
ELSE()
MESSAGE(STATUS "No build tag specified.")
SET(MultiMC_BUILD_TAG custom)
ENDIF()
# Architecture detection
@ -162,11 +163,15 @@ gui/settingsdialog.h
gui/newinstancedialog.h
gui/logindialog.h
gui/taskdialog.h
gui/browserdialog.h
gui/aboutdialog.h
gui/consolewindow.h
data/version.h
data/userinfo.h
data/loginresponse.h
data/appsettings.h
data/minecraftprocess.h
data/plugin/pluginmanager.h
@ -196,6 +201,7 @@ data/loginresponse.cpp
data/appsettings.cpp
data/plugin/pluginmanager.cpp
data/minecraftprocess.cpp
gui/mainwindow.cpp
gui/modeditwindow.cpp
@ -203,6 +209,9 @@ gui/settingsdialog.cpp
gui/newinstancedialog.cpp
gui/logindialog.cpp
gui/taskdialog.cpp
gui/browserdialog.cpp
gui/aboutdialog.cpp
gui/consolewindow.cpp
java/javautils.cpp
java/annotations.cpp
@ -221,6 +230,9 @@ gui/settingsdialog.ui
gui/newinstancedialog.ui
gui/logindialog.ui
gui/taskdialog.ui
gui/browserdialog.ui
gui/aboutdialog.ui
gui/consolewindow.ui
)
@ -258,7 +270,7 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32
${MULTIMC_SOURCES} ${MULTIMC_HEADERS} ${MULTIMC_UI} ${MULTIMC_QRC} ${MULTIMC_RCS})
# Link
QT5_USE_MODULES(MultiMC Widgets Network)
QT5_USE_MODULES(MultiMC Widgets Network WebKitWidgets)
TARGET_LINK_LIBRARIES(MultiMC quazip patchlib
libmmcutil libmmcsettings libmmcinst
${MultiMC_LINK_ADDITIONAL_LIBS})
@ -266,6 +278,10 @@ ADD_DEPENDENCIES(MultiMC MultiMCLauncher libmmcutil libmmcsettings libmmcinst)
################################ INSTALLATION AND PACKAGING ################################
# use QtCreator's QTDIR var
IF(DEFINED ENV{QTDIR})
SET(Qt5_DIR $ENV{QTDIR})
ENDIF()
######## Plugin and library folders ########
@ -282,7 +298,7 @@ ENDIF()
IF(APPLE)
SET(PLUGIN_DEST_DIR MultiMC.app/Contents/MacOS)
SET(QTCONF_DEST_DIR MultiMC.app/Contents/Resources)
SET(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
SET(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
ENDIF()
SET(QT_PLUGINS_DIR ${Qt5_DIR}/plugins)

246
data/minecraftprocess.cpp Normal file
View File

@ -0,0 +1,246 @@
/* Copyright 2013 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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 "minecraftprocess.h"
#include <QDataStream>
#include <QFile>
#include <QDir>
#include <QImage>
#include <QProcessEnvironment>
#include "instance.h"
#include "osutils.h"
#include "pathutils.h"
#define LAUNCHER_FILE "MultiMCLauncher.jar"
#define IBUS "@im=ibus"
// commandline splitter
QStringList MinecraftProcess::splitArgs(QString args)
{
QStringList argv;
QString current;
bool escape = false;
QChar inquotes;
for (int i=0; i<args.length(); i++)
{
QChar cchar = args.at(i);
// \ escaped
if (escape) {
current += cchar;
escape = false;
// in "quotes"
} else if (!inquotes.isNull()) {
if (cchar == 0x5C)
escape = true;
else if (cchar == inquotes)
inquotes = 0;
else
current += cchar;
// otherwise
} else {
if (cchar == 0x20) {
if (!current.isEmpty()) {
argv << current;
current.clear();
}
} else if (cchar == 0x22 || cchar == 0x27)
inquotes = cchar;
else
current += cchar;
}
}
if (!current.isEmpty())
argv << current;
return argv;
}
// prepare tools
inline void MinecraftProcess::extractIcon(InstancePtr inst, QString destination)
{
QImage(":/icons/instances/" + inst->iconKey()).save(destination);
}
inline void MinecraftProcess::extractLauncher(QString destination)
{
QFile(":/launcher/launcher.jar").copy(destination);
}
void MinecraftProcess::prepare(InstancePtr inst)
{
extractLauncher(PathCombine(inst->minecraftDir(), LAUNCHER_FILE));
extractIcon(inst, PathCombine(inst->minecraftDir(), "icon.png"));
}
// constructor
MinecraftProcess::MinecraftProcess(InstancePtr inst, QString user, QString session, ConsoleWindow *console) :
m_instance(inst), m_user(user), m_session(session), m_console(console)
{
connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(finish(int, QProcess::ExitStatus)));
// prepare the process environment
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
#ifdef LINUX
// Strip IBus
if (env.value("XMODIFIERS").contains(IBUS))
env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, ""));
#endif
// export some infos
env.insert("INST_NAME", inst->name());
env.insert("INST_ID", inst->id());
env.insert("INST_DIR", QDir(inst->rootDir()).absolutePath());
this->setProcessEnvironment(env);
m_prepostlaunchprocess.setProcessEnvironment(env);
// set the cwd
QDir mcDir(inst->minecraftDir());
this->setWorkingDirectory(mcDir.absolutePath());
m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath());
// std channels
connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr()));
connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut()));
}
// console window
void MinecraftProcess::on_stdErr()
{
if (m_console != nullptr)
m_console->write(readAllStandardError(), ConsoleWindow::ERROR);
}
void MinecraftProcess::on_stdOut()
{
if (m_console != nullptr)
m_console->write(readAllStandardOutput(), ConsoleWindow::DEFAULT);
}
void MinecraftProcess::log(QString text)
{
if (m_console != nullptr)
m_console->write(text);
else
qDebug(qPrintable(text));
}
// exit handler
void MinecraftProcess::finish(int code, ExitStatus status)
{
if (status != NormalExit)
{
//TODO: error handling
}
log("Minecraft exited.");
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
// run post-exit
if (!m_instance->getPostExitCommand().isEmpty())
{
m_prepostlaunchprocess.start(m_instance->getPostExitCommand());
m_prepostlaunchprocess.waitForFinished();
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
{
//TODO: error handling
}
}
if (m_console != nullptr)
m_console->setMayClose(true);
emit ended();
}
void MinecraftProcess::launch()
{
if (!m_instance->getPreLaunchCommand().isEmpty())
{
m_prepostlaunchprocess.start(m_instance->getPreLaunchCommand());
m_prepostlaunchprocess.waitForFinished();
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
{
//TODO: error handling
return;
}
}
m_instance->setLastLaunch();
prepare(m_instance);
genArgs();
log(QString("Minecraft folder is: '%1'").arg(workingDirectory()));
log(QString("Instance launched with arguments: '%1'").arg(m_arguments.join("' '")));
start(m_instance->getJavaPath(), m_arguments);
if (!waitForStarted())
{
//TODO: error handling
}
if(m_console != nullptr)
m_console->setMayClose(false);
}
void MinecraftProcess::genArgs()
{
// start fresh
m_arguments.clear();
// window size
QString windowSize;
if (m_instance->getLaunchMaximized())
windowSize = "max";
else
windowSize = QString("%1x%2").arg(m_instance->getMinecraftWinWidth()).arg(m_instance->getMinecraftWinHeight());
// window title
QString windowTitle;
windowTitle.append("MultiMC: ").append(m_instance->name());
// Java arguments
m_arguments.append(splitArgs(m_instance->getJvmArgs()));
#ifdef OSX
// OSX dock icon and name
m_arguments << "-Xdock:icon=icon.png";
m_arguments << QString("-Xdock:name=\"%1\"").arg(windowTitle);
#endif
// lwjgl
QString lwjgl = m_instance->lwjglVersion();
if (lwjgl != "Mojang")
lwjgl = QDir(settings->getLWJGLDir() + "/" + lwjgl).absolutePath();
// launcher arguments
m_arguments << QString("-Xms%1m").arg(m_instance->getMinMemAlloc());
m_arguments << QString("-Xmx%1m").arg(m_instance->getMaxMemAlloc());
m_arguments << "-jar" << LAUNCHER_FILE;
m_arguments << m_user;
m_arguments << m_session;
m_arguments << windowTitle;
m_arguments << windowSize;
m_arguments << lwjgl;
}

98
data/minecraftprocess.h Normal file
View File

@ -0,0 +1,98 @@
/* Copyright 2013 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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.
*/
#ifndef MINECRAFTPROCESS_H
#define MINECRAFTPROCESS_H
#include <QProcess>
#include "gui/consolewindow.h"
#include "instance.h"
/**
* @file data/minecraftprocess.h
* @brief The MinecraftProcess class
*/
class MinecraftProcess : public QProcess
{
Q_OBJECT
public:
/**
* @brief MinecraftProcess constructor
* @param inst the Instance pointer to launch
* @param user the minecraft username
* @param session the minecraft session id
* @param console the instance console window
*/
MinecraftProcess(InstancePtr inst, QString user, QString session, ConsoleWindow *console);
/**
* @brief launch minecraft
*/
void launch();
/**
* @brief extract the instance icon
* @param inst the instance
* @param destination the destination path
*/
static inline void extractIcon(InstancePtr inst, QString destination);
/**
* @brief extract the MultiMC launcher.jar
* @param destination the destination path
*/
static inline void extractLauncher(QString destination);
/**
* @brief prepare the launch by extracting icon and launcher
* @param inst the instance
*/
static void prepare(InstancePtr inst);
/**
* @brief split a string into argv items like a shell would do
* @param args the argument string
* @return a QStringList containing all arguments
*/
static QStringList splitArgs(QString args);
signals:
/**
* @brief emitted when mc has finished and the PostLaunchCommand was run
*/
void ended();
protected:
ConsoleWindow *m_console;
InstancePtr m_instance;
QString m_user;
QString m_session;
QProcess m_prepostlaunchprocess;
QStringList m_arguments;
void genArgs();
void log(QString text);
protected slots:
void finish(int, QProcess::ExitStatus status);
void on_stdErr();
void on_stdOut();
};
#endif // MINECRAFTPROCESS_H

23
gui/aboutdialog.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include <QIcon>
#include <QApplication>
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
ui->icon->setPixmap(QIcon(":/icons/multimc/scalable/apps/multimc.svg").pixmap(64));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
QApplication::instance()->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt()));
}
AboutDialog::~AboutDialog()
{
delete ui;
}

22
gui/aboutdialog.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent = 0);
~AboutDialog();
private:
Ui::AboutDialog *ui;
};
#endif // ABOUTDIALOG_H

288
gui/aboutdialog.ui Normal file
View File

@ -0,0 +1,288 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>450</width>
<height>400</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>450</width>
<height>400</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="baseSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="title">
<property name="font">
<font>
<pointsize>15</pointsize>
</font>
</property>
<property name="text">
<string>MultiMC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="aboutPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>153</height>
</rect>
</property>
<attribute name="label">
<string>About</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="aboutLabel">
<property name="text">
<string>MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple installations of Minecraft at once.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="copyLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>© 2013 MultiMC Contributors</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="urlLabel">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/Forkk/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/Forkk/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="creditsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>153</height>
</rect>
</property>
<attribute name="label">
<string>Credits</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTextEdit" name="creditsText">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Andrew Okin &amp;lt;&lt;a href=&quot;mailto:forkk@forkk.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;forkk@forkk.net&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Petr Mrázek &amp;lt;&lt;a href=&quot;mailto:peterix@gmail.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;peterix@gmail.com&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Orochimarufan &amp;lt;&lt;a href=&quot;mailto:orochimarufan.x3@gmail.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;orochimarufan.x3@gmail.com&lt;/span&gt;&lt;/a&gt;&amp;gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="licensePage">
<attribute name="label">
<string>License</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTextEdit" name="licenseText">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2012 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;MultiMC uses bspatch, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Copyright 2003-2005 Colin Percival&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;All rights reserved&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Redistribution and use in source and binary forms, with or without&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;modification, are permitted providing that the following conditions&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;are met: &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;1. Redistributions of source code must retain the above copyright&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; notice, this list of conditions and the following disclaimer.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;2. Redistributions in binary form must reproduce the above copyright&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; notice, this list of conditions and the following disclaimer in the&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; documentation and/or other materials provided with the distribution.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="aboutQt">
<property name="text">
<string>About Qt</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

76
gui/browserdialog.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "browserdialog.h"
#include "ui_browserdialog.h"
#include <QtWebKit/QWebHistory>
BrowserDialog::BrowserDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::BrowserDialog),
m_pageTitleInWindowTitle(true),
m_windowTitleFormat("%1")
{
ui->setupUi(this);
ui->webView->setPage(new QWebPage());
refreshWindowTitle();
resize(800, 600);
}
BrowserDialog::~BrowserDialog()
{
delete ui;
}
// Navigation Buttons
void BrowserDialog::on_btnBack_clicked()
{
ui->webView->back();
}
void BrowserDialog::on_btnForward_clicked()
{
ui->webView->forward();
}
void BrowserDialog::on_webView_urlChanged(const QUrl &url)
{
Q_UNUSED(url);
//qDebug("urlChanged");
ui->btnBack->setEnabled(ui->webView->history()->canGoBack());
ui->btnForward->setEnabled(ui->webView->history()->canGoForward());
}
// Window Title Magic
void BrowserDialog::refreshWindowTitle()
{
//qDebug("refreshTitle");
if (m_pageTitleInWindowTitle)
setWindowTitle(m_windowTitleFormat.arg(ui->webView->title()));
else
setWindowTitle(m_windowTitleFormat);
}
void BrowserDialog::setPageTitleInWindowTitle(bool enable)
{
m_pageTitleInWindowTitle = enable;
refreshWindowTitle();
}
void BrowserDialog::setWindowTitleFormat(QString format)
{
m_windowTitleFormat = format;
refreshWindowTitle();
}
void BrowserDialog::on_webView_titleChanged(const QString &title)
{
//qDebug("titleChanged");
if (m_pageTitleInWindowTitle)
setWindowTitle(m_windowTitleFormat.arg(title));
}
// Public access Methods
void BrowserDialog::load(const QUrl &url)
{
//qDebug("load");
ui->webView->setUrl(url);
}

41
gui/browserdialog.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef BROWSERDIALOG_H
#define BROWSERDIALOG_H
#include <QDialog>
namespace Ui {
class BrowserDialog;
}
class BrowserDialog : public QDialog
{
Q_OBJECT
public:
explicit BrowserDialog(QWidget *parent = 0);
~BrowserDialog();
void load(const QUrl &url);
void setPageTitleInWindowTitle(bool enable);
bool pageTitleInWindowTitle(void) { return m_pageTitleInWindowTitle; }
void setWindowTitleFormat(QString format);
QString windowTitleFormat(void) { return m_windowTitleFormat; }
private:
Ui::BrowserDialog *ui;
bool m_pageTitleInWindowTitle;
QString m_windowTitleFormat;
void refreshWindowTitle(void);
private slots:
void on_btnBack_clicked(void);
void on_btnForward_clicked(void);
void on_webView_urlChanged(const QUrl &url);
void on_webView_titleChanged(const QString &title);
};
#endif // BROWSERDIALOG_H

92
gui/browserdialog.ui Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BrowserDialog</class>
<widget class="QDialog" name="BrowserDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>535</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="toolbarLayout">
<item>
<widget class="QCommandLinkButton" name="btnBack">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Back</string>
</property>
<property name="icon">
<iconset theme="go-previous"/>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="btnForward">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Forward</string>
</property>
<property name="icon">
<iconset theme="go-next"/>
</property>
</widget>
</item>
<item>
<spacer name="toolbarSpacer_1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QWebView" name="webView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKitWidgets/QWebView</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

73
gui/consolewindow.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "consolewindow.h"
#include "ui_consolewindow.h"
#include <QScrollBar>
ConsoleWindow::ConsoleWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::ConsoleWindow),
m_mayclose(true)
{
ui->setupUi(this);
}
ConsoleWindow::~ConsoleWindow()
{
delete ui;
}
void ConsoleWindow::writeColor(QString text, const char *color)
{
// append a paragraph
if (color != nullptr)
ui->text->appendHtml(QString("<font color=%1>%2</font>").arg(color).arg(text));
else
ui->text->appendPlainText(text);
// scroll down
QScrollBar *bar = ui->text->verticalScrollBar();
bar->setValue(bar->maximum());
}
void ConsoleWindow::write(QString data, WriteMode mode)
{
if (data.endsWith('\n'))
data = data.left(data.length()-1);
QStringList paragraphs = data.split('\n');
QListIterator<QString> iter(paragraphs);
if (mode == MULTIMC)
while(iter.hasNext())
writeColor(iter.next(), "blue");
else if (mode == ERROR)
while(iter.hasNext())
writeColor(iter.next(), "red");
else
while(iter.hasNext())
writeColor(iter.next());
}
void ConsoleWindow::clear()
{
ui->text->clear();
}
void ConsoleWindow::on_closeButton_clicked()
{
close();
}
void ConsoleWindow::setMayClose(bool mayclose)
{
m_mayclose = mayclose;
if (mayclose)
ui->closeButton->setEnabled(true);
else
ui->closeButton->setEnabled(false);
}
void ConsoleWindow::closeEvent(QCloseEvent * event)
{
if(!m_mayclose)
event->ignore();
else
QDialog::closeEvent(event);
}

69
gui/consolewindow.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef CONSOLEWINDOW_H
#define CONSOLEWINDOW_H
#include <QDialog>
namespace Ui {
class ConsoleWindow;
}
class ConsoleWindow : public QDialog
{
Q_OBJECT
public:
/**
* @brief The WriteMode enum
* defines how stuff is displayed
*/
enum WriteMode {
DEFAULT,
ERROR,
MULTIMC
};
explicit ConsoleWindow(QWidget *parent = 0);
~ConsoleWindow();
/**
* @brief specify if the window is allowed to close
* @param mayclose
* used to keep it alive while MC runs
*/
void setMayClose(bool mayclose);
public slots:
/**
* @brief write a string
* @param data the string
* @param mode the WriteMode
* lines have to be put through this as a whole!
*/
void write(QString data, WriteMode mode=MULTIMC);
/**
* @brief write a colored paragraph
* @param data the string
* @param color the css color name
* this will only insert a single paragraph.
* \n are ignored. a real \n is always appended.
*/
void writeColor(QString data, const char *color=nullptr);
/**
* @brief clear the text widget
*/
void clear();
private slots:
void on_closeButton_clicked();
protected:
void closeEvent(QCloseEvent *);
private:
Ui::ConsoleWindow *ui;
bool m_mayclose;
};
#endif // CONSOLEWINDOW_H

66
gui/consolewindow.ui Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConsoleWindow</class>
<widget class="QDialog" name="ConsoleWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>MultiMC Console</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="text">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="plainText">
<string notr="true"/>
</property>
<property name="centerOnScroll">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,4 +1,8 @@
/* Copyright 2013 MultiMC Contributors
*
* Authors: Andrew Okin
* Peterix
* Orochimarufan <orochimarufan.x3@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,17 +22,23 @@
#include <QMenu>
#include <QMessageBox>
#include <QInputDialog>
#include <QApplication>
#include <QDesktopServices>
#include <QUrl>
#include <QDir>
#include <QFileInfo>
#include "osutils.h"
#include "userutils.h"
#include "pathutils.h"
#include "gui/settingsdialog.h"
#include "gui/newinstancedialog.h"
#include "gui/logindialog.h"
#include "gui/taskdialog.h"
#include "gui/browserdialog.h"
#include "gui/aboutdialog.h"
#include "instancelist.h"
#include "data/appsettings.h"
@ -94,17 +104,20 @@ void MainWindow::on_actionSettings_triggered()
void MainWindow::on_actionReportBug_triggered()
{
QDesktopServices::openUrl(QUrl("http://bugs.forkk.net/"));
//QDesktopServices::openUrl(QUrl("http://bugs.forkk.net/"));
openWebPage(QUrl("http://bugs.forkk.net/"));
}
void MainWindow::on_actionNews_triggered()
{
QDesktopServices::openUrl(QUrl("http://news.forkk.net/"));
//QDesktopServices::openUrl(QUrl("http://news.forkk.net/"));
openWebPage(QUrl("http://news.forkk.net/"));
}
void MainWindow::on_actionAbout_triggered()
{
AboutDialog dialog(this);
dialog.exec();
}
void MainWindow::on_mainToolBar_visibilityChanged(bool)
@ -163,6 +176,26 @@ void MainWindow::onLoginComplete(LoginResponse response)
arg(response.username(), response.sessionID()));
}
// Create A Desktop Shortcut
void MainWindow::on_actionMakeDesktopShortcut_triggered()
{
QString name("Test");
name = QInputDialog::getText(this, tr("MultiMC Shortcut"), tr("Enter a Shortcut Name."), QLineEdit::Normal, name);
Util::createShortCut(Util::getDesktopDir(), QApplication::instance()->applicationFilePath(), QStringList() << "-dl" << QDir::currentPath() << "test", name, "application-x-octet-stream");
QMessageBox::warning(this, "Not useful", "A Dummy Shortcut was created. it will not do anything productive");
}
// BrowserDialog
void MainWindow::openWebPage(QUrl url)
{
BrowserDialog *browser = new BrowserDialog(this);
browser->load(url);
browser->exec();
}
void openInDefaultProgram(QString filename)
{
QDesktopServices::openUrl("file:///" + QFileInfo(filename).absolutePath());

View File

@ -36,6 +36,9 @@ public:
void closeEvent(QCloseEvent *event);
// Browser Dialog
void openWebPage(QUrl url);
private slots:
void on_actionAbout_triggered();
@ -62,6 +65,8 @@ private slots:
void on_actionLaunchInstance_triggered();
void on_actionMakeDesktopShortcut_triggered();
void doLogin(const QString& errorMsg = "");

View File

@ -305,4 +305,7 @@ private:
QString m_rootDir;
};
// pointer for lazy people
typedef QSharedPointer<Instance> InstancePtr;
#endif // INSTANCE_H

View File

@ -28,7 +28,7 @@ Instance::Instance(const QString &rootDir, QObject *parent) :
QString Instance::id() const
{
return QFileInfo(rootDir()).baseName();
return QFileInfo(rootDir()).fileName();
}
QString Instance::rootDir() const
@ -50,13 +50,9 @@ QString Instance::minecraftDir() const
QFileInfo dotMCDir(PathCombine(rootDir(), ".minecraft"));
if (dotMCDir.exists() && !mcDir.exists())
{
return dotMCDir.path();
}
return dotMCDir.filePath();
else
{
return mcDir.path();
}
return mcDir.filePath();
}
QString Instance::binDir() const

View File

@ -1,5 +1,20 @@
project(libmmcutil)
######## Set compiler flags ########
IF(APPLE)
# assume clang 4.1.0+, add C++0x/C++11 stuff
message(STATUS "Using APPLE CMAKE_CXX_FLAGS")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++")
ELSEIF(UNIX)
# assume GCC, add C++0x/C++11 stuff
MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
ELSEIF(MINGW)
MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
ENDIF()
# Find Qt
find_package(Qt5Core REQUIRED)
@ -14,6 +29,8 @@ include/apputils.h
include/pathutils.h
include/osutils.h
include/userutils.h
include/cmdutils.h
include/inifile.h
@ -24,6 +41,8 @@ include/siglist_impl.h
SET(LIBUTIL_SOURCES
src/pathutils.cpp
src/osutils.cpp
src/userutils.cpp
src/cmdutils.cpp
src/inifile.cpp
)

239
libutil/include/cmdutils.h Normal file
View File

@ -0,0 +1,239 @@
/* Copyright 2013 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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.
*/
#ifndef CMDUTILS_H
#define CMDUTILS_H
#include <exception>
#include <QString>
#include <QVariant>
#include <QHash>
#include <QStringList>
/**
* @file libutil/include/cmdutils.h
* @brief commandline parsing utilities
*/
namespace Util {
namespace Commandline {
/**
* @brief The FlagStyle enum
* Specifies how flags are decorated
*/
enum class FlagStyle {
GNU, /**< --option and -o (GNU Style) */
Unix, /**< -option and -o (Unix Style) */
Windows, /**< /option and /o (Windows Style) */
#ifdef Q_OS_WIN32
Default = Windows
#else
Default = GNU
#endif
};
/**
* @brief The ArgumentStyle enum
*/
enum class ArgumentStyle {
Space, /**< --option=value */
Equals, /**< --option value */
SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32
Default = Equals
#else
Default = SpaceAndEquals
#endif
};
/**
* @brief The ParsingError class
*/
class ParsingError : public std::exception
{
public:
ParsingError(const QString &what);
ParsingError(const ParsingError &e);
~ParsingError() throw() {}
const char *what() const throw();
QString qwhat() const;
private:
QString m_what;
};
/**
* @brief The Parser class
*/
class Parser
{
public:
/**
* @brief Parser constructor
* @param flagStyle the FlagStyle to use in this Parser
* @param argStyle the ArgumentStyle to use in this Parser
*/
Parser(FlagStyle flagStyle=FlagStyle::Default, ArgumentStyle argStyle=ArgumentStyle::Default);
/**
* @brief set the flag style
* @param style
*/
void setFlagStyle(FlagStyle style);
/**
* @brief get the flag style
* @return
*/
FlagStyle flagStyle();
/**
* @brief set the argument style
* @param style
*/
void setArgumentStyle(ArgumentStyle style);
/**
* @brief get the argument style
* @return
*/
ArgumentStyle argumentStyle();
/**
* @brief define a boolean switch
* @param name the parameter name
* @param def the default value
*/
void addSwitch(QString name, bool def=false);
/**
* @brief define an option that takes an additional argument
* @param name the parameter name
* @param def the default value
*/
void addOption(QString name, QVariant def=QVariant());
/**
* @brief define a positional argument
* @param name the parameter name
* @param required wether this argument is required
* @param def the default value
*/
void addArgument(QString name, bool required=true, QVariant def=QVariant());
/**
* @brief adds a flag to an existing parameter
* @param name the (existing) parameter name
* @param flag the flag character
* @see addSwitch addArgument addOption
* Note: any one parameter can only have one flag
*/
void addShortOpt(QString name, QChar flag);
/**
* @brief adds documentation to a Parameter
* @param name the parameter name
* @param metavar a string to be displayed as placeholder for the value
* @param doc a QString containing the documentation
* Note: on positional arguments, metavar replaces the name as displayed.
* on options , metavar replaces the value placeholder
*/
void addDocumentation(QString name, QString doc, QString metavar=QString());
/**
* @brief generate a help message
* @param progName the program name to use in the help message
* @param helpIndent how much the parameter documentation should be indented
* @param flagsInUsage whether we should use flags instead of options in the usage
* @return a help message
*/
QString compileHelp(QString progName, int helpIndent=22, bool flagsInUsage=true);
/**
* @brief generate a short usage message
* @param progName the program name to use in the usage message
* @param useFlags whether we should use flags instead of options
* @return a usage message
*/
QString compileUsage(QString progName, bool useFlags=true);
/**
* @brief parse
* @param argv a QStringList containing the program ARGV
* @return a QHash mapping argument names to their values
*/
QHash<QString, QVariant> parse(QStringList argv);
/**
* @brief clear all definitions
*/
void clear();
~Parser();
private:
FlagStyle m_flagStyle;
ArgumentStyle m_argStyle;
enum class OptionType {
Switch,
Option
};
// Important: the common part MUST BE COMMON ON ALL THREE structs
struct CommonDef {
QString name;
QString doc;
QString metavar;
QVariant def;
};
struct OptionDef {
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// option
OptionType type;
QChar flag;
};
struct PositionalDef {
// common
QString name;
QString doc;
QString metavar;
QVariant def;
// positional
bool required;
};
QHash<QString, OptionDef *> m_options;
QHash<QChar, OptionDef *> m_flags;
QHash<QString, CommonDef *> m_params;
QList<PositionalDef *> m_positionals;
QList<OptionDef *> m_optionList;
void getPrefix(QString &opt, QString &flag);
};
}
}
#endif // CMDUTILS_H

View File

@ -71,7 +71,7 @@ public:
virtual QList<T> &operator =(const QList<T> &other);
protected:
// Signal emitted after an item is added to the list.
// Contains a reference to item and the item's new index.
virtual void onItemAdded(const T &item, int index) = 0;

View File

@ -0,0 +1,17 @@
#ifndef USERUTILS_H
#define USERUTILS_H
#include <QString>
namespace Util
{
// Get the Directory representing the User's Desktop
QString getDesktopDir();
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
// call it *name* and assign it the icon *icon*
// return true if operation succeeded
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
}
#endif // USERUTILS_H

434
libutil/src/cmdutils.cpp Normal file
View File

@ -0,0 +1,434 @@
/* Copyright 2013 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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 "include/cmdutils.h"
/**
* @file libutil/src/cmdutils.cpp
*/
namespace Util {
namespace Commandline {
Parser::Parser(FlagStyle flagStyle, ArgumentStyle argStyle)
{
m_flagStyle = flagStyle;
m_argStyle = argStyle;
}
// styles setter/getter
void Parser::setArgumentStyle(ArgumentStyle style)
{
m_argStyle = style;
}
ArgumentStyle Parser::argumentStyle()
{
return m_argStyle;
}
void Parser::setFlagStyle(FlagStyle style)
{
m_flagStyle = style;
}
FlagStyle Parser::flagStyle()
{
return m_flagStyle;
}
// setup methods
void Parser::addSwitch(QString name, bool def)
{
if (m_params.contains(name))
throw "Name not unique";
OptionDef *param = new OptionDef;
param->type = OptionType::Switch;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
}
void Parser::addOption(QString name, QVariant def)
{
if (m_params.contains(name))
throw "Name not unique";
OptionDef *param = new OptionDef;
param->type = OptionType::Option;
param->name = name;
param->metavar = QString("<%1>").arg(name);
param->def = def;
m_options[name] = param;
m_params[name] = (CommonDef *)param;
m_optionList.append(param);
}
void Parser::addArgument(QString name, bool required, QVariant def)
{
if (m_params.contains(name))
throw "Name not unique";
PositionalDef *param = new PositionalDef;
param->name = name;
param->def = def;
param->required = required;
param->metavar = name;
m_positionals.append(param);
m_params[name] = (CommonDef *)param;
}
void Parser::addDocumentation(QString name, QString doc, QString metavar)
{
if (!m_params.contains(name))
throw "Name does not exist";
CommonDef *param = m_params[name];
param->doc = doc;
if (!metavar.isNull())
param->metavar = metavar;
}
void Parser::addShortOpt(QString name, QChar flag)
{
if (!m_params.contains(name))
throw "Name does not exist";
if (!m_options.contains(name))
throw "Name is not an Option or Swtich";
OptionDef *param = m_options[name];
m_flags[flag] = param;
param->flag = flag;
}
// help methods
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
{
QStringList help;
help << compileUsage(progName, useFlags) << "\r\n";
// positionals
if (!m_positionals.isEmpty())
{
help << "\r\n";
help << "Positional arguments:\r\n";
QListIterator<PositionalDef *> it2(m_positionals);
while(it2.hasNext())
{
PositionalDef *param = it2.next();
help << " " << param->metavar;
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
help << param->doc << "\r\n";
}
}
// Options
if (!m_optionList.isEmpty())
{
help << "\r\n";
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
help << "Options & Switches:\r\n";
QListIterator<OptionDef *> it(m_optionList);
while(it.hasNext())
{
OptionDef *option = it.next();
help << " ";
int nameLength = optPrefix.length() + option->name.length();
if (!option->flag.isNull())
{
nameLength += 3 + flagPrefix.length();
help << flagPrefix << option->flag << ", ";
}
help << optPrefix << option->name;
if (option->type == OptionType::Option)
{
QString arg = QString("%1%2").arg(((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
nameLength += arg.length();
help << arg;
}
help << " " << QString(helpIndent - nameLength - 1, ' ');
help << option->doc << "\r\n";
}
}
return help.join("");
}
QString Parser::compileUsage(QString progName, bool useFlags)
{
QStringList usage;
usage << "Usage: " << progName;
QString optPrefix, flagPrefix;
getPrefix(optPrefix, flagPrefix);
// options
QListIterator<OptionDef *> it(m_optionList);
while(it.hasNext())
{
OptionDef *option = it.next();
usage << " [";
if (!option->flag.isNull() && useFlags)
usage << flagPrefix << option->flag;
else
usage << optPrefix << option->name;
if (option->type == OptionType::Option)
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
usage << "]";
}
// arguments
QListIterator<PositionalDef *> it2(m_positionals);
while(it2.hasNext())
{
PositionalDef *param = it2.next();
usage << " " << (param->required ? "<" : "[");
usage << param->metavar;
usage << (param->required ? ">" : "]");
}
return usage.join("");
}
// parsing
QHash<QString, QVariant> Parser::parse(QStringList argv)
{
QHash<QString, QVariant> map;
QStringListIterator it(argv);
QString programName = it.next();
QString optionPrefix;
QString flagPrefix;
QListIterator<PositionalDef *> positionals(m_positionals);
QStringList expecting;
getPrefix(optionPrefix, flagPrefix);
while (it.hasNext())
{
QString arg = it.next();
if (!expecting.isEmpty())
// we were expecting an argument
{
QString name = expecting.first();
if (map.contains(name))
throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
map[name] = QVariant(arg);
expecting.removeFirst();
continue;
}
if (arg.startsWith(optionPrefix))
// we have an option
{
//qDebug("Found option %s", qPrintable(arg));
QString name = arg.mid(optionPrefix.length());
QString equals;
if ((m_argStyle == ArgumentStyle::Equals || m_argStyle == ArgumentStyle::SpaceAndEquals) && name.contains("="))
{
int i = name.indexOf("=");
equals = name.mid(i+1);
name = name.left(i);
}
if (m_options.contains(name))
{
if (map.contains(name))
throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
OptionDef *option = m_options[name];
if (option->type == OptionType::Switch)
map[name] = true;
else //if (option->type == OptionType::Option)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(name);
else if (!equals.isNull())
map[name] = equals;
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(name);
else
throw ParsingError(QString("Option %2%1 reqires an argument.").arg(name, optionPrefix));
}
continue;
}
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
}
if (arg.startsWith(flagPrefix))
// we have (a) flag(s)
{
//qDebug("Found flags %s", qPrintable(arg));
QString flags = arg.mid(flagPrefix.length());
QString equals;
if ((m_argStyle == ArgumentStyle::Equals || m_argStyle == ArgumentStyle::SpaceAndEquals) && flags.contains("="))
{
int i = flags.indexOf("=");
equals = flags.mid(i+1);
flags = flags.left(i);
}
for (int i = 0; i < flags.length(); i++)
{
QChar flag = flags.at(i);
if (!m_flags.contains(flag))
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
OptionDef *option = m_flags[flag];
if (map.contains(option->name))
throw ParsingError(QString("Option %2%1 was given multiple times").arg(option->name, optionPrefix));
if (option->type == OptionType::Switch)
map[option->name] = true;
else //if (option->type == OptionType::Option)
{
if (m_argStyle == ArgumentStyle::Space)
expecting.append(option->name);
else if (!equals.isNull())
if (i == flags.length()-1)
map[option->name] = equals;
else
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option %1 not last flag in %4%3").arg(option->name, flag, flags, flagPrefix));
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
expecting.append(option->name);
else
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)").arg(option->name, flag, flagPrefix));
}
}
continue;
}
// must be a positional argument
if (!positionals.hasNext())
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
PositionalDef *param = positionals.next();
map[param->name] = arg;
}
// check if we're missing something
if (!expecting.isEmpty())
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(expecting.join(QString(", ")+optionPrefix), optionPrefix));
while (positionals.hasNext())
{
PositionalDef *param = positionals.next();
if (param->required)
throw ParsingError(QString("Missing required positional argument '%1'").arg(param->name));
else
map[param->name] = param->def;
}
// fill out gaps
QListIterator<OptionDef *> iter(m_optionList);
while (iter.hasNext())
{
OptionDef *option = iter.next();
if (!map.contains(option->name))
map[option->name] = option->def;
}
return map;
}
//clear defs
void Parser::clear()
{
m_flags.clear();
m_params.clear();
m_options.clear();
QMutableListIterator<OptionDef *> it(m_optionList);
while(it.hasNext())
{
OptionDef *option = it.next();
it.remove();
delete option;
}
QMutableListIterator<PositionalDef *> it2(m_positionals);
while(it2.hasNext())
{
PositionalDef *arg = it2.next();
it2.remove();
delete arg;
}
}
//Destructor
Parser::~Parser()
{
clear();
}
//getPrefix
void Parser::getPrefix(QString &opt, QString &flag)
{
if (m_flagStyle == FlagStyle::Windows)
opt = flag = "/";
else if (m_flagStyle == FlagStyle::Unix)
opt = flag = "-";
//else if (m_flagStyle == FlagStyle::GNU)
else {
opt = "--";
flag = "-";
}
}
// ParsingError
ParsingError::ParsingError(const QString &what)
{
m_what = what;
}
ParsingError::ParsingError(const ParsingError &e)
{
m_what = e.m_what;
}
const char *ParsingError::what() const throw()
{
return m_what.toLocal8Bit().constData();
}
QString ParsingError::qwhat() const
{
return m_what;
}
}
}

113
libutil/src/userutils.cpp Normal file
View File

@ -0,0 +1,113 @@
#include "include/userutils.h"
#include <QStandardPaths>
#include <QFile>
#include <QTextStream>
#include "include/osutils.h"
#include "include/pathutils.h"
// Win32 crap
#if WINDOWS
#include <windows.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
#include <shlobj.h>
bool called_coinit = false;
HRESULT CreateLink(LPCSTR linkPath, LPCWSTR targetPath, LPCWSTR args)
{
HRESULT hres;
if (!called_coinit)
{
hres = CoInitialize(NULL);
called_coinit = true;
if (!SUCCEEDED(hres))
{
qWarning("Failed to initialize COM. Error 0x%08X", hres);
return hres;
}
}
IShellLink* link;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&link);
if (SUCCEEDED(hres))
{
IPersistFile* persistFile;
link->SetPath(targetPath);
link->SetArguments(args);
hres = link->QueryInterface(IID_IPersistFile, (LPVOID*)&persistFile);
if (SUCCEEDED(hres))
{
WCHAR wstr[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
hres = persistFile->Save(wstr, TRUE);
persistFile->Release();
}
link->Release();
}
return hres;
}
#endif
QString Util::getDesktopDir()
{
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
// Cross-platform Shortcut creation
bool Util::createShortCut(QString location, QString dest, QStringList args, QString name, QString icon)
{
#if LINUX
location = PathCombine(location, name + ".desktop");
qDebug("location: %s", qPrintable(location));
QFile f(location);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
QString argstring;
if (!args.empty())
argstring = " '" + args.join("' '") + "'";
stream << "[Desktop Entry]" << "\n";
stream << "Type=Application" << "\n";
stream << "TryExec=" << dest.toLocal8Bit() << "\n";
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
stream << "Icon=" << icon.toLocal8Bit() << "\n";
stream.flush();
f.close();
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
return true;
#elif WINDOWS
QFile file(path, name + ".lnk");
WCHAR *file_w;
WCHAR *dest_w;
WCHAR *args_w;
file.fileName().toWCharArray(file_w);
dest.toWCharArray(dest_w);
args.toWCharArray(args_w);
return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
#else
qWarning("Desktop Shortcuts not supported on your platform!");
return false;
#endif
}

195
main.cpp
View File

@ -1,5 +1,7 @@
/* Copyright 2013 MultiMC Contributors
*
* Authors: Andrew Okin
* Orochimarufan <orochimarufan.x3@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,34 +16,221 @@
* limitations under the License.
*/
#include "gui/mainwindow.h"
#include <iostream>
#include <QApplication>
#include <QDir>
#include "gui/mainwindow.h"
#include "gui/logindialog.h"
#include "gui/taskdialog.h"
#include "gui/consolewindow.h"
#include "data/appsettings.h"
#include "instancelist.h"
#include "data/loginresponse.h"
#include "tasks/logintask.h"
#include "data/minecraftprocess.h"
#include "data/plugin/pluginmanager.h"
#include "pathutils.h"
#include "cmdutils.h"
#include "config.h"
using namespace Util::Commandline;
// Commandline instance launcher
class InstanceLauncher : public QObject
{
Q_OBJECT
private:
InstanceList instances;
QString instId;
InstancePtr instance;
MinecraftProcess *proc;
ConsoleWindow *console;
public:
InstanceLauncher(QString instId) : QObject(), instances(settings->getInstanceDir())
{
this->instId = instId;
}
private:
InstancePtr findInstance(QString instId)
{
QListIterator<InstancePtr> iter(instances);
InstancePtr inst;
while(iter.hasNext())
{
inst = iter.next();
if (inst->id() == instId)
break;
}
if (inst->id() != instId)
return InstancePtr();
else
return iter.peekPrevious();
}
private slots:
void onTerminated()
{
std::cout << "Minecraft exited" << std::endl;
QApplication::instance()->quit();
}
void onLoginComplete(LoginResponse response)
{
// TODO: console
console = new ConsoleWindow();
proc = new MinecraftProcess(instance, response.getUsername(), response.getSessionID(), console);
//if (instance->getShowConsole())
console->show();
connect(proc, SIGNAL(ended()), SLOT(onTerminated()));
proc->launch();
}
void doLogin(const QString &errorMsg)
{
LoginDialog* loginDlg = new LoginDialog(nullptr, errorMsg);
if (loginDlg->exec())
{
UserInfo uInfo(loginDlg->getUsername(), loginDlg->getPassword());
TaskDialog* tDialog = new TaskDialog(nullptr);
LoginTask* loginTask = new LoginTask(uInfo, tDialog);
connect(loginTask, SIGNAL(loginComplete(LoginResponse)),
SLOT(onLoginComplete(LoginResponse)), Qt::QueuedConnection);
connect(loginTask, SIGNAL(loginFailed(QString)),
SLOT(doLogin(QString)), Qt::QueuedConnection);
tDialog->exec(loginTask);
}
//onLoginComplete(LoginResponse("Offline","Offline", 1));
}
public:
int launch()
{
std::cout << "Loading Instances..." << std::endl;
instances.loadList();
std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
instance = findInstance(instId);
if (instance.isNull())
{
std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;
return 1;
}
std::cout << "Logging in..." << std::endl;
doLogin("");
return QApplication::instance()->exec();
}
};
int main(int argc, char *argv[])
{
// initialize Qt
QApplication app(argc, argv);
app.setOrganizationName("Forkk");
app.setApplicationName("MultiMC 5");
// Print app header
std::cout << "MultiMC 5" << std::endl;
std::cout << "(c) 2013 MultiMC Contributors" << std::endl << std::endl;
// Commandline parsing
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
// --help
parser.addSwitch("help");
parser.addShortOpt("help", 'h');
parser.addDocumentation("help", "display this help and exit.");
// --version
parser.addSwitch("version");
parser.addShortOpt("version", 'V');
parser.addDocumentation("version", "display program version and exit.");
// --dir
parser.addOption("dir", app.applicationDirPath());
parser.addShortOpt("dir", 'd');
parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of the binary location (use '.' for current)");
// --update
parser.addOption("update");
parser.addShortOpt("update", 'u');
parser.addDocumentation("update", "replaces the given file with the running executable", "<path>");
// --quietupdate
parser.addSwitch("quietupdate");
parser.addShortOpt("quietupdate", 'U');
parser.addDocumentation("quietupdate", "doesn't restart MultiMC after installing updates");
// --launch
parser.addOption("launch");
parser.addShortOpt("launch", 'l');
parser.addDocumentation("launch", "tries to launch the given instance", "<inst>");
// parse the arguments
QHash<QString, QVariant> args;
try {
args = parser.parse(app.arguments());
} catch(ParsingError e) {
std::cerr << "CommandLineError: " << e.what() << std::endl;
std::cerr << "Try '%1 -h' to get help on MultiMC's command line parameters." << std::endl;
return 1;
}
// display help and exit
if (args["help"].toBool()) {
std::cout << qPrintable(parser.compileHelp(app.arguments()[0]));
return 0;
}
// display version and exit
if (args["version"].toBool()) {
std::cout << "Version " << VERSION_STR << std::endl;
std::cout << "Git " << GIT_COMMIT << std::endl;
std::cout << "Tag: " << JENKINS_BUILD_TAG << " " << (ARCH==x64?"x86_64":"x86") << std::endl;
return 0;
}
// update
// Note: cwd is always the current executable path!
if (!args["update"].isNull())
{
std::cout << "Performing MultiMC update: " << qPrintable(args["update"].toString()) << std::endl;
QString cwd = QDir::currentPath();
QDir::setCurrent(app.applicationDirPath());
QFile file(app.applicationFilePath());
file.copy(args["update"].toString());
if(args["quietupdate"].toBool())
return 0;
QDir::setCurrent(cwd);
}
// change directory
QDir::setCurrent(args["dir"].toString());
// load settings
settings = new AppSettings(&app);
// Register meta types.
qRegisterMetaType<LoginResponse>("LoginResponse");
// Initialize plugins.
PluginManager::get().loadPlugins(PathCombine(qApp->applicationDirPath(), "plugins"));
PluginManager::get().initInstanceTypes();
// launch instance.
if (!args["launch"].isNull())
return InstanceLauncher(args["launch"].toString()).launch();
// show main window
MainWindow mainWin;
mainWin.show();
// loop
return app.exec();
}
#include "main.moc"

View File

@ -28,6 +28,10 @@
<file alias="infinity">resources/icons/multimc.svg</file>
</qresource>
<qresource prefix="/launcher">
<file alias="launcherjar">resources/MultiMCLauncher.jar</file>
<file alias="launcher.jar">resources/MultiMCLauncher.jar</file>
</qresource>
<qresource prefix="/icons/multimc">
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
<file alias="index.theme">resources/XdgIcon.theme</file>
</qresource>
</RCC>

12
resources/XdgIcon.theme Normal file
View File

@ -0,0 +1,12 @@
[Icon Theme]
Name=MultiMC
Comment=MultiMC Default Icons
Inherits=default
Directories=scalable/apps
[scalable/apps]
Size=48
Type=scalable
MinSize=1
MaxSize=512
Context=Applications