Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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

Conflicts:
	CMakeLists.txt
	gui/mainwindow.cpp
  • Loading branch information...
commit ebb2c54975e3f0b7b891532e8e72d2ef760f96c4 2 parents 168ed3e + 1f13f0c
@Forkk authored
Showing with 1,501 additions and 487 deletions.
  1. +9 −1 CMakeLists.txt
  2. +39 −38 gui/consolewindow.cpp
  3. +35 −44 gui/consolewindow.h
  4. +127 −0 gui/iconcache.cpp
  5. +43 −0 gui/iconcache.h
  6. +9 −3 gui/instancedelegate.cpp
  7. +38 −5 gui/instancemodel.cpp
  8. +6 −1 gui/instancemodel.h
  9. +50 −1 gui/logindialog.cpp
  10. +5 −1 gui/logindialog.h
  11. +18 −15 gui/logindialog.ui
  12. +79 −7 gui/mainwindow.cpp
  13. +10 −1 gui/mainwindow.h
  14. +23 −21 gui/settingsdialog.ui
  15. +6 −2 java/constants.h
  16. +60 −48 java/javautils.cpp
  17. +22 −3 java/javautils.h
  18. +10 −0 libgroupview/include/kcategorizedview.h
  19. +24 −7 libgroupview/src/kcategorizedview.cpp
  20. +122 −120 libgroupview/src/kcategorizedview_p.h
  21. +31 −3 libmultimc/include/instance.h
  22. +37 −7 libmultimc/include/instancelist.h
  23. +79 −55 libmultimc/include/minecraftprocess.h
  24. +6 −0 libmultimc/src/instance.cpp
  25. +155 −5 libmultimc/src/instancelist.cpp
  26. +39 −17 libmultimc/src/minecraftprocess.cpp
  27. +10 −1 libsettings/CMakeLists.txt
  28. +92 −0 libsettings/include/keyring.h
  29. +4 −1 libsettings/src/basicsettingsobject.cpp
  30. +4 −1 libsettings/src/inisettingsobject.cpp
  31. +63 −0 libsettings/src/keyring.cpp
  32. +8 −1 libsettings/src/setting.cpp
  33. +104 −0 libsettings/src/stubkeyring.cpp
  34. +41 −0 libsettings/src/stubkeyring.h
  35. +0 −3  libutil/CMakeLists.txt
  36. +7 −10 libutil/include/cmdutils.h
  37. +8 −8 libutil/src/cmdutils.cpp
  38. +2 −18 main.cpp
  39. +3 −0  multimc.qrc
  40. BIN  resources/catbgrnd2.png
  41. +6 −6 resources/icons/instances/clucker.svg
  42. +7 −33 resources/icons/instances/skeleton.svg
  43. +60 −0 test.cpp
View
10 CMakeLists.txt
@@ -31,7 +31,6 @@ ENDIF()
# First, include header overrides
include_directories(hacks)
-
######## 3rd Party Libs ########
# Find the required Qt parts
@@ -48,6 +47,7 @@ find_package(ZLIB REQUIRED)
# Add quazip
add_subdirectory(quazip)
+include_directories(quazip)
# Add bspatch
add_subdirectory(patchlib)
@@ -173,6 +173,7 @@ gui/consolewindow.h
gui/instancemodel.h
gui/instancedelegate.h
gui/versionselectdialog.h
+gui/iconcache.h
multimc_pragma.h
@@ -202,6 +203,7 @@ gui/consolewindow.cpp
gui/instancemodel.cpp
gui/instancedelegate.cpp
gui/versionselectdialog.cpp
+gui/iconcache.cpp
java/javautils.cpp
java/annotations.cpp
@@ -263,6 +265,12 @@ libUtil libSettings libMultiMC libGroupView
${MultiMC_LINK_ADDITIONAL_LIBS})
ADD_DEPENDENCIES(MultiMC MultiMCLauncher libUtil libSettings libMultiMC libGroupView)
+IF(DEFINED MMC_KEYRING_TEST)
+# test.cpp
+ADD_EXECUTABLE(Test test.cpp)
+QT5_USE_MODULES(Test Core)
+TARGET_LINK_LIBRARIES(Test libUtil libSettings)
+ENDIF()
################################ INSTALLATION AND PACKAGING ################################
# use QtCreator's QTDIR var
View
77 gui/consolewindow.cpp
@@ -4,70 +4,71 @@
#include <QScrollBar>
ConsoleWindow::ConsoleWindow(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::ConsoleWindow),
- m_mayclose(true)
+ QDialog(parent),
+ ui(new Ui::ConsoleWindow),
+ m_mayclose(true)
{
- ui->setupUi(this);
+ ui->setupUi(this);
}
ConsoleWindow::~ConsoleWindow()
{
- delete ui;
+ 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());
+ // 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)
+void ConsoleWindow::write(QString data, MessageLevel::Enum 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());
+ if (data.endsWith('\n'))
+ data = data.left(data.length()-1);
+ QStringList paragraphs = data.split('\n');
+ QListIterator<QString> iter(paragraphs);
+ if (mode == MessageLevel::MultiMC)
+ while(iter.hasNext())
+ writeColor(iter.next(), "blue");
+ else if (mode == MessageLevel::Error)
+ while(iter.hasNext())
+ writeColor(iter.next(), "red");
+ // TODO: implement other MessageLevels
+ else
+ while(iter.hasNext())
+ writeColor(iter.next());
}
void ConsoleWindow::clear()
{
- ui->text->clear();
+ ui->text->clear();
}
void ConsoleWindow::on_closeButton_clicked()
{
- close();
+ close();
}
void ConsoleWindow::setMayClose(bool mayclose)
{
- m_mayclose = mayclose;
- if (mayclose)
- ui->closeButton->setEnabled(true);
- else
- ui->closeButton->setEnabled(false);
+ 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);
+ if(!m_mayclose)
+ event->ignore();
+ else
+ QDialog::closeEvent(event);
}
View
79 gui/consolewindow.h
@@ -2,6 +2,7 @@
#define CONSOLEWINDOW_H
#include <QDialog>
+#include "minecraftprocess.h"
namespace Ui {
class ConsoleWindow;
@@ -9,61 +10,51 @@ class ConsoleWindow;
class ConsoleWindow : public QDialog
{
- Q_OBJECT
+ Q_OBJECT
public:
- /**
- * @brief The WriteMode enum
- * defines how stuff is displayed
- */
- enum WriteMode {
- DEFAULT,
- ERROR,
- MULTIMC
- };
+ explicit ConsoleWindow(QWidget *parent = 0);
+ ~ConsoleWindow();
- 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);
+ /**
+ * @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();
+ /**
+ * @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, MessageLevel::Enum level=MessageLevel::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();
+ void on_closeButton_clicked();
protected:
- void closeEvent(QCloseEvent *);
+ void closeEvent(QCloseEvent *);
private:
- Ui::ConsoleWindow *ui;
- bool m_mayclose;
+ Ui::ConsoleWindow *ui;
+ bool m_mayclose;
};
#endif // CONSOLEWINDOW_H
View
127 gui/iconcache.cpp
@@ -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"
View
43 gui/iconcache.h
@@ -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
12 gui/instancedelegate.cpp
@@ -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
43 gui/instancemodel.cpp
@@ -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
7 gui/instancemodel.h
@@ -22,9 +22,14 @@ class InstanceModel : public QAbstractListModel
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
51 gui/logindialog.cpp
@@ -15,12 +15,18 @@
#include "logindialog.h"
#include "ui_logindialog.h"
+#include "keyring.h"
LoginDialog::LoginDialog(QWidget *parent, const QString& loginErrMsg) :
QDialog(parent),
ui(new Ui::LoginDialog)
{
ui->setupUi(this);
+ //FIXME: translateable?
+ ui->usernameTextBox->lineEdit()->setPlaceholderText(QApplication::translate("LoginDialog", "Name", 0));
+
+ connect(ui->usernameTextBox, SIGNAL(currentTextChanged(QString)), this, SLOT(userTextChanged(QString)));
+ connect(ui->forgetButton, SIGNAL(clicked(bool)), this, SLOT(forgetCurrentUser()));
if (loginErrMsg.isEmpty())
ui->loginErrorLabel->setVisible(false);
@@ -33,6 +39,10 @@ LoginDialog::LoginDialog(QWidget *parent, const QString& loginErrMsg) :
resize(minimumSizeHint());
layout()->setSizeConstraint(QLayout::SetFixedSize);
+ Keyring * k = Keyring::instance();
+ QStringList accounts = k->getStoredAccounts("minecraft");
+ ui->usernameTextBox->addItems(accounts);
+
}
LoginDialog::~LoginDialog()
@@ -42,10 +52,49 @@ LoginDialog::~LoginDialog()
QString LoginDialog::getUsername() const
{
- return ui->usernameTextBox->text();
+ return ui->usernameTextBox->currentText();
}
QString LoginDialog::getPassword() const
{
return ui->passwordTextBox->text();
}
+
+void LoginDialog::forgetCurrentUser()
+{
+ Keyring * k = Keyring::instance();
+ QString acct = ui->usernameTextBox->currentText();
+ k->removeStoredAccount("minecraft", acct);
+ ui->passwordTextBox->clear();
+ int index = ui->usernameTextBox->findText(acct);
+ if(index != -1)
+ ui->usernameTextBox->removeItem(index);
+}
+
+void LoginDialog::userTextChanged ( const QString& user )
+{
+ Keyring * k = Keyring::instance();
+ QString acct = ui->usernameTextBox->currentText();
+ QString passwd = k->getPassword("minecraft",acct);
+ ui->passwordTextBox->setText(passwd);
+}
+
+
+void LoginDialog::accept()
+{
+ bool saveName = ui->rememberUsernameCheckbox->isChecked();
+ bool savePass = ui->rememberPasswordCheckbox->isChecked();
+ if(saveName)
+ {
+ Keyring * k = Keyring::instance();
+ if(savePass)
+ {
+ k->storePassword("minecraft",getUsername(),getPassword());
+ }
+ else
+ {
+ k->storePassword("minecraft",getUsername(),QString());
+ }
+ }
+ QDialog::accept();
+}
View
6 gui/logindialog.h
@@ -32,7 +32,11 @@ class LoginDialog : public QDialog
QString getUsername() const;
QString getPassword() const;
-
+
+public slots:
+ virtual void accept();
+ virtual void userTextChanged(const QString& user);
+ virtual void forgetCurrentUser();
private:
Ui::LoginDialog *ui;
};
View
33 gui/logindialog.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>365</width>
- <height>145</height>
+ <width>476</width>
+ <height>168</height>
</rect>
</property>
<property name="windowTitle">
@@ -31,9 +31,9 @@
</widget>
</item>
<item row="0" column="1">
- <widget class="QLineEdit" name="usernameTextBox">
- <property name="placeholderText">
- <string>Username</string>
+ <widget class="QComboBox" name="usernameTextBox">
+ <property name="editable">
+ <bool>true</bool>
</property>
</widget>
</item>
@@ -54,20 +54,23 @@
</property>
</widget>
</item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="checkboxLayout">
- <item>
- <widget class="QPushButton" name="forceUpdateButton">
- <property name="text">
- <string>&amp;Force Update</string>
+ <item row="0" column="2" rowspan="2">
+ <widget class="QPushButton" name="forgetButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="checkable">
- <bool>true</bool>
+ <property name="text">
+ <string>Forget</string>
</property>
</widget>
</item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="checkboxLayout">
<item>
<widget class="QCheckBox" name="rememberUsernameCheckbox">
<property name="sizePolicy">
View
86 gui/mainwindow.cpp
@@ -40,6 +40,7 @@
#include "gui/browserdialog.h"
#include "gui/aboutdialog.h"
#include "gui/versionselectdialog.h"
+#include "gui/consolewindow.h"
#include "kcategorizedview.h"
#include "kcategorydrawer.h"
@@ -50,6 +51,7 @@
#include "logintask.h"
#include <instance.h>
+#include "minecraftprocess.h"
#include "instancemodel.h"
#include "instancedelegate.h"
@@ -65,11 +67,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 );
@@ -82,6 +98,7 @@ MainWindow::MainWindow ( QWidget *parent ) :
auto delegate = new ListViewDelegate();
view->setItemDelegate(delegate);
view->setSpacing(10);
+ view->setUniformItemWidths(true);
model = new InstanceModel ( instList,this );
proxymodel = new InstanceProxyModel ( this );
@@ -101,7 +118,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()
@@ -126,6 +150,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() );
@@ -196,13 +232,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());
}
}
@@ -226,9 +280,27 @@ void MainWindow::doLogin ( QString inst, const QString& errorMsg )
void MainWindow::onLoginComplete ( QString inst, LoginResponse response )
{
+ // TODO: console
+ console = new ConsoleWindow();
+ auto instance = instList.getInstanceById(inst);
+ if(instance)
+ {
+ proc = new MinecraftProcess(instance, response.username(), response.sessionID());
+
+ console->show();
+ //connect(proc, SIGNAL(ended()), SLOT(onTerminated()));
+ connect(proc, SIGNAL(log(QString,MessageLevel::Enum)), console, SLOT(write(QString,MessageLevel::Enum)));
+ proc->launch();
+ }
+ else
+ {
+
+ }
+ /*
QMessageBox::information ( this, "Login Successful",
QString ( "Logged in as %1 with session ID %2. Instance: %3" ).
arg ( response.username(), response.sessionID(), inst ) );
+ */
}
void MainWindow::onLoginFailed ( QString inst, const QString& errorMsg )
View
11 gui/mainwindow.h
@@ -26,6 +26,8 @@ class InstanceModel;
class InstanceProxyModel;
class KCategorizedView;
class KCategoryDrawer;
+class MinecraftProcess;
+class ConsoleWindow;
namespace Ui
{
@@ -44,14 +46,19 @@ class MainWindow : public QMainWindow
// 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();
@@ -91,6 +98,8 @@ public slots:
InstanceModel * model;
InstanceProxyModel * proxymodel;
InstanceList instList;
+ MinecraftProcess *proc;
+ ConsoleWindow *console;
};
#endif // MAINWINDOW_H
View
44 gui/settingsdialog.ui
@@ -6,10 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
- <width>400</width>
- <height>420</height>
+ <width>453</width>
+ <height>563</height>
</rect>
</property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="windowTitle">
<string>Settings</string>
</property>
@@ -400,31 +406,27 @@
<item row="1" column="1">
<widget class="QLineEdit" name="postExitCmdTextBox"/>
</item>
- <item row="2" column="0" colspan="2">
- <widget class="QLabel" name="labelCustomCmdsDescription">
- <property name="text">
- <string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
</layout>
</widget>
</item>
<item>
- <spacer name="verticalSpacerJava">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
+ <widget class="QLabel" name="labelCustomCmdsDescription">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
+ <property name="text">
+ <string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
</property>
- </spacer>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
</item>
</layout>
</widget>
View
8 java/constants.h
@@ -7,7 +7,7 @@ namespace java
class constant
{
public:
- enum type_t:uint8_t
+ enum type_t : uint8_t
{
j_hole = 0, // HACK: this is a hole in the array, because java is crazy
j_string_data = 1,
@@ -22,6 +22,7 @@ namespace java
j_interface_methodref = 11,
j_nameandtype = 12
} type;
+
constant(util::membuffer & buf )
{
buf.read(type);
@@ -66,10 +67,12 @@ namespace java
break;
}
}
+
constant(int fake)
{
type = j_hole;
}
+
std::string toString()
{
std::ostringstream ss;
@@ -143,6 +146,7 @@ namespace java
} name_and_type;
};
};
+
/**
* A helper class that represents the custom container used in Java class file for storage of constants
*/
@@ -181,7 +185,7 @@ namespace java
index++;
}
}
- };
+ }
typedef std::vector<java::constant> container_type;
/**
* Access constants based on jar file index numbers (index of the first element is 1)
View
108 java/javautils.cpp
@@ -1,69 +1,81 @@
+/* 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 "multimc_pragma.h"
#include "classfile.h"
#include "javautils.h"
-//#include <wx/zipstrm.h>
-#include <memory>
-//#include <wx/wfstream.h>
-//#include "mcversionlist.h"
+
+#include <QFile>
+#include <quazipfile.h>
namespace javautils
{
-QString GetMinecraftJarVersion(QString jar)
+
+QString GetMinecraftJarVersion(QString jarName)
{
- return "Unknown";
- /*
- wxString fullpath = jar.GetFullPath();
- wxString version = MCVer_Unknown;
- if(!jar.FileExists())
+ QString version = MCVer_Unknown;
+
+ // check if minecraft.jar exists
+ QFile jar(jarName);
+ if (!jar.exists())
return version;
- std::auto_ptr<wxZipEntry> entry;
- // convert the local name we are looking for into the internal format
- wxString name = wxZipEntry::GetInternalName("net/minecraft/client/Minecraft.class",wxPATH_UNIX);
- // open the zip
- wxFFileInputStream inStream(jar.GetFullPath());
- wxZipInputStream zipIn(inStream);
+ // open minecraft.jar
+ QuaZip zip(&jar);
+ if (!zip.open(QuaZip::mdUnzip))
+ return version;
- // call GetNextEntry() until the required internal name is found
- do
- {
- entry.reset(zipIn.GetNextEntry());
- }
- while (entry.get() != NULL && entry->GetInternalName() != name);
- auto myentry = entry.get();
- if (myentry == NULL)
+ // open Minecraft.class
+ zip.setCurrentFile("net/minecraft/client/Minecraft.class", QuaZip::csSensitive);
+ QuaZipFile Minecraft(&zip);
+ if (!Minecraft.open(QuaZipFile::ReadOnly))
return version;
-
- // we got the entry, read the data
- std::size_t size = myentry->GetSize();
- char *classdata = new char[size];
- zipIn.Read(classdata,size);
- try
- {
- char * temp = classdata;
- java::classfile Minecraft_jar(temp,size);
- auto cnst = Minecraft_jar.constants;
- auto iter = cnst.begin();
- while (iter != cnst.end())
+
+ // read Minecraft.class
+ qint64 size = Minecraft.size();
+ char *classfile = new char[size];
+ Minecraft.read(classfile, size);
+
+ // parse Minecraft.class
+ try {
+ char *temp = classfile;
+ java::classfile MinecraftClass(temp, size);
+ java::constant_pool constants = MinecraftClass.constants;
+ for(java::constant_pool::container_type::const_iterator iter=constants.begin();
+ iter != constants.end(); iter++)
{
const java::constant & constant = *iter;
- if(constant.type != java::constant::j_string_data)
- {
- iter++;
+ if (constant.type != java::constant::j_string_data)
continue;
- }
- auto & str = constant.str_data;
- const char * lookfor = "Minecraft Minecraft "; // length = 20
- if(str.compare(0,20,lookfor) == 0)
+ const std::string & str = constant.str_data;
+ if (str.compare(0, 20, "Minecraft Minecraft ") == 0)
{
version = str.substr(20).data();
break;
}
- iter++;
}
- } catch(java::classfile_exception &){}
- delete[] classdata;
+ } catch(java::classfile_exception &) {}
+
+ // clean up
+ delete[] classfile;
+ Minecraft.close();
+ zip.close();
+ jar.close();
+
return version;
- */
}
-}
+
+}
View
25 java/javautils.h
@@ -1,9 +1,28 @@
+/* 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.
+ */
#pragma once
#include <QString>
+
+#define MCVer_Unknown "Unknown"
+
namespace javautils
{
- /*
- * Get the version from a minecraft.jar by parsing its class files. Expensive!
+ /**
+ * @brief Get the version from a minecraft.jar by parsing its class files. Expensive!
*/
QString GetMinecraftJarVersion(QString jar);
-}
+}
View
10 libgroupview/include/kcategorizedview.h
@@ -208,6 +208,16 @@ class LIBGROUPVIEW_EXPORT KCategorizedView
*/
virtual void reset();
+ /**
+ * Signify that all item delegates size hints return the same fixed size
+ */
+ void setUniformItemWidths(bool enable);
+
+ /**
+ * Do all item delegate size hints return the same fixed size?
+ */
+ bool uniformItemWidths() const;
+
protected:
/**
* Reimplemented from QWidget.
View
31 libgroupview/src/kcategorizedview.cpp
@@ -115,6 +115,7 @@ KCategorizedView::Private::Private ( KCategorizedView *q )
, hoveredIndex ( QModelIndex() )
, pressedPosition ( QPoint() )
, rubberBandRect ( QRect() )
+ , constantItemWidth( 0 )
{
}
@@ -447,11 +448,12 @@ void KCategorizedView::Private::leftToRightVisualRect ( const QModelIndex &index
}
else
{
- if ( q->uniformItemSizes() )
+ if ( q->uniformItemSizes() /*|| q->uniformItemWidths()*/ )
{
const int relativeRow = index.row() - firstIndexRow;
const QSize itemSize = q->sizeHintForIndex ( index );
- const int maxItemsPerRow = qMax ( ( viewportWidth() - q->spacing() ) / ( itemSize.width() + q->spacing() ), 1 );
+ //HACK: Why is the -2 needed?
+ const int maxItemsPerRow = qMax ( ( viewportWidth() - q->spacing() - 2 ) / ( itemSize.width() + q->spacing() ), 1 );
if ( q->layoutDirection() == Qt::LeftToRight )
{
item.topLeft.rx() = ( relativeRow % maxItemsPerRow ) * itemSize.width() + blockPos.x() + categoryDrawer->leftMargin();
@@ -478,7 +480,8 @@ void KCategorizedView::Private::leftToRightVisualRect ( const QModelIndex &index
Q_FOREVER
{
prevIndex = proxyModel->index ( prevIndex.row() - 1, q->modelColumn(), q->rootIndex() );
- const QRect tempRect = q->visualRect ( prevIndex );
+ QRect tempRect = q->visualRect ( prevIndex );
+ tempRect = mapFromViewport ( tempRect );
if ( tempRect.topLeft().y() < prevRect.topLeft().y() )
{
break;
@@ -622,6 +625,18 @@ void KCategorizedView::setModel ( QAbstractItemModel *model )
}
}
+
+void KCategorizedView::setUniformItemWidths(bool enable)
+{
+ d->constantItemWidth = enable;
+}
+
+
+bool KCategorizedView::uniformItemWidths() const
+{
+ return d->constantItemWidth;
+}
+
void KCategorizedView::setGridSize ( const QSize &size )
{
setGridSizeOwn ( size );
@@ -1294,13 +1309,14 @@ QModelIndex KCategorizedView::moveCursor ( CursorAction cursorAction,
}
case MoveDown:
{
- if ( d->hasGrid() || uniformItemSizes() )
+ if ( d->hasGrid() || uniformItemSizes() || uniformItemWidths() )
{
const QModelIndex current = currentIndex();
const QSize itemSize = d->hasGrid() ? gridSize()
: sizeHintForIndex ( current );
const Private::Block &block = d->blocks[d->categoryForIndex ( current )];
- const int maxItemsPerRow = qMax ( d->viewportWidth() / itemSize.width(), 1 );
+ //HACK: Why is the -2 needed?
+ const int maxItemsPerRow = qMax ( ( d->viewportWidth() - spacing() - 2 ) / ( itemSize.width() + spacing() ), 1 );
const bool canMove = current.row() + maxItemsPerRow < block.firstIndex.row() +
block.items.count();
@@ -1334,13 +1350,14 @@ QModelIndex KCategorizedView::moveCursor ( CursorAction cursorAction,
}
case MoveUp:
{
- if ( d->hasGrid() || uniformItemSizes() )
+ if ( d->hasGrid() || uniformItemSizes() || uniformItemWidths() )
{
const QModelIndex current = currentIndex();
const QSize itemSize = d->hasGrid() ? gridSize()
: sizeHintForIndex ( current );
const Private::Block &block = d->blocks[d->categoryForIndex ( current )];
- const int maxItemsPerRow = qMax ( d->viewportWidth() / itemSize.width(), 1 );
+ //HACK: Why is the -2 needed?
+ const int maxItemsPerRow = qMax ( ( d->viewportWidth() - spacing() - 2 ) / ( itemSize.width() + spacing() ), 1 );
const bool canMove = current.row() - maxItemsPerRow >= block.firstIndex.row();
if ( canMove )
View
242 libgroupview/src/kcategorizedview_p.h
@@ -32,126 +32,128 @@ class KCategoryDrawerV3;
class KCategorizedView::Private
{
public:
- struct Block;
- struct Item;
-
- Private(KCategorizedView *q);
- ~Private();
-
- /**
- * @return whether this view has all required elements to be categorized.
- */
- bool isCategorized() const;
-
- /**
- * @return the block rect for the representative @p representative.
- */
- QStyleOptionViewItemV4 blockRect(const QModelIndex &representative);
-
- /**
- * Returns the first and last element that intersects with rect.
- *
- * @note see that here we cannot take out items between first and last (as we could
- * do with the rubberband).
- *
- * Complexity: O(log(n)) where n is model()->rowCount().
- */
- QPair<QModelIndex, QModelIndex> intersectingIndexesWithRect(const QRect &rect) const;
-
- /**
- * Returns the position of the block of @p category.
- *
- * Complexity: O(n) where n is the number of different categories when the block has been
- * marked as in quarantine. O(1) the rest of the times (the vast majority).
- */
- QPoint blockPosition(const QString &category);
-
- /**
- * Returns the height of the block determined by @p category.
- */
- int blockHeight(const QString &category);
-
- /**
- * Returns the actual viewport width.
- */
- int viewportWidth() const;
-
- /**
- * Marks all elements as in quarantine.
- *
- * Complexity: O(n) where n is model()->rowCount().
- *
- * @warning this is an expensive operation
- */
- void regenerateAllElements();
-
- /**
- * Update internal information, and keep sync with the real information that the model contains.
- */
- void rowsInserted(const QModelIndex &parent, int start, int end);
-
- /**
- * Returns @p rect in viewport terms, taking in count horizontal and vertical offsets.
- */
- QRect mapToViewport(const QRect &rect) const;
-
- /**
- * Returns @p rect in absolute terms, converted from viewport position.
- */
- QRect mapFromViewport(const QRect &rect) const;
-
- /**
- * Returns the height of the highest element in last row. This is only applicable if there is
- * no grid set and uniformItemSizes is false.
- *
- * @param block in which block are we searching. Necessary to stop the search if we hit the
- * first item in this block.
- */
- int highestElementInLastRow(const Block &block) const;
-
- /**
- * Returns whether the view has a valid grid size.
- */
- bool hasGrid() const;
-
- /**
- * Returns the category for the given index.
- */
- QString categoryForIndex(const QModelIndex &index) const;
-
- /**
- * Updates the visual rect for item when flow is LeftToRight.
- */
- void leftToRightVisualRect(const QModelIndex &index, Item &item,
- const Block &block, const QPoint &blockPos) const;
-
- /**
- * Updates the visual rect for item when flow is TopToBottom.
- * @note we only support viewMode == ListMode in this case.
- */
- void topToBottomVisualRect(const QModelIndex &index, Item &item,
- const Block &block, const QPoint &blockPos) const;
-
- /**
- * Called when expand or collapse has been clicked on the category drawer.
- */
- void _k_slotCollapseOrExpandClicked(QModelIndex);
-
- KCategorizedView *q;
- KCategorizedSortFilterProxyModel *proxyModel;
- KCategoryDrawer *categoryDrawer;
- int categorySpacing;
- bool alternatingBlockColors;
- bool collapsibleBlocks;
-
- Block *hoveredBlock;
- QString hoveredCategory;
- QModelIndex hoveredIndex;
-
- QPoint pressedPosition;
- QRect rubberBandRect;
-
- QHash<QString, Block> blocks;
+ struct Block;
+ struct Item;
+
+ Private(KCategorizedView *q);
+ ~Private();
+
+ /**
+ * @return whether this view has all required elements to be categorized.
+ */
+ bool isCategorized() const;
+
+ /**
+ * @return the block rect for the representative @p representative.
+ */
+ QStyleOptionViewItemV4 blockRect(const QModelIndex &representative);
+
+ /**
+ * Returns the first and last element that intersects with rect.
+ *
+ * @note see that here we cannot take out items between first and last (as we could
+ * do with the rubberband).
+ *
+ * Complexity: O(log(n)) where n is model()->rowCount().
+ */
+ QPair<QModelIndex, QModelIndex> intersectingIndexesWithRect(const QRect &rect) const;
+
+ /**
+ * Returns the position of the block of @p category.
+ *
+ * Complexity: O(n) where n is the number of different categories when the block has been
+ * marked as in quarantine. O(1) the rest of the times (the vast majority).
+ */
+ QPoint blockPosition(const QString &category);
+
+ /**
+ * Returns the height of the block determined by @p category.
+ */
+ int blockHeight(const QString &category);
+
+ /**
+ * Returns the actual viewport width.
+ */
+ int viewportWidth() const;
+
+ /**
+ * Marks all elements as in quarantine.
+ *
+ * Complexity: O(n) where n is model()->rowCount().
+ *
+ * @warning this is an expensive operation
+ */
+ void regenerateAllElements();
+
+ /**
+ * Update internal information, and keep sync with the real information that the model contains.
+ */
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+
+ /**
+ * Returns @p rect in viewport terms, taking in count horizontal and vertical offsets.
+ */
+ QRect mapToViewport(const QRect &rect) const;
+
+ /**
+ * Returns @p rect in absolute terms, converted from viewport position.
+ */
+ QRect mapFromViewport(const QRect &rect) const;
+
+ /**
+ * Returns the height of the highest element in last row. This is only applicable if there is
+ * no grid set and uniformItemSizes is false.
+ *
+ * @param block in which block are we searching. Necessary to stop the search if we hit the
+ * first item in this block.
+ */
+ int highestElementInLastRow(const Block &block) const;
+
+ /**
+ * Returns whether the view has a valid grid size.
+ */
+ bool hasGrid() const;
+
+ /**
+ * Returns the category for the given index.
+ */
+ QString categoryForIndex(const QModelIndex &index) const;
+
+ /**
+ * Updates the visual rect for item when flow is LeftToRight.
+ */
+ void leftToRightVisualRect(const QModelIndex &index, Item &item,
+ const Block &block, const QPoint &blockPos) const;
+
+ /**
+ * Updates the visual rect for item when flow is TopToBottom.
+ * @note we only support viewMode == ListMode in this case.
+ */
+ void topToBottomVisualRect(const QModelIndex &index, Item &item,
+ const Block &block, const QPoint &blockPos) const;
+
+ /**
+ * Called when expand or collapse has been clicked on the category drawer.
+ */
+ void _k_slotCollapseOrExpandClicked(QModelIndex);
+
+ KCategorizedView *q;
+ KCategorizedSortFilterProxyModel *proxyModel;
+ KCategoryDrawer *categoryDrawer;
+ int categorySpacing;
+ bool alternatingBlockColors;
+ bool collapsibleBlocks;
+ bool constantItemWidth;
+
+ Block *hoveredBlock;
+ QString hoveredCategory;
+ QModelIndex hoveredIndex;
+
+ QPoint pressedPosition;
+ QRect rubberBandRect;
+
+ QHash<QString, Block> blocks;
};
#endif // KCATEGORIZEDVIEW_P_H
+
View
34 libmultimc/include/instance.h
@@ -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 @@ class LIBMULTIMC_EXPORT Instance : public QObject
//// 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 @@ class LIBMULTIMC_EXPORT Instance : public QObject
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 @@ class LIBMULTIMC_EXPORT Instance : public QObject
*/
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
44 libmultimc/include/instancelist.h
@@ -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 @@ class LIBMULTIMC_EXPORT InstanceList : public QObject, public SigList< QSharedPo
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
134 libmultimc/include/minecraftprocess.h
@@ -6,7 +6,7 @@
* 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
+ * 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,
@@ -24,73 +24,97 @@
#include "libmmc_config.h"
/**
+ * @brief the MessageLevel Enum
+ * defines what level a message is
+ */
+namespace MessageLevel {
+enum LIBMULTIMC_EXPORT Enum {
+ MultiMC, /**< MultiMC Messages */
+ Debug, /**< Debug Messages */
+ Info, /**< Info Messages */
+ Message, /**< Standard Messages */
+ Warning, /**< Warnings */
+ Error, /**< Errors */
+ Fatal /**< Fatal Errors */
+};
+}
+
+/**
* @file data/minecraftprocess.h
* @brief The MinecraftProcess class
*/
class LIBMULTIMC_EXPORT MinecraftProcess : public QProcess
{
- Q_OBJECT
+ 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);
-
- /**
- * @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);
+ /**
+ * @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);
+
+ /**
+ * @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();
+ /**
+ * @brief emitted when mc has finished and the PostLaunchCommand was run
+ */
+ void ended();
+
+ /**
+ * @brief emitted when we want to log something
+ * @param text the text to log
+ * @param level the level to log at
+ */
+ void log(QString text, MessageLevel::Enum level=MessageLevel::MultiMC);
protected:
- InstancePtr m_instance;
- QString m_user;
- QString m_session;
- QProcess m_prepostlaunchprocess;
- QStringList m_arguments;
+ InstancePtr m_instance;
+ QString m_user;
+ QString m_session;
+ QString m_err_leftover;
+ QString m_out_leftover;
+ QProcess m_prepostlaunchprocess;
+ QStringList m_arguments;
- void genArgs();
- void log(QString text);
+ void genArgs();
protected slots:
- void finish(int, QProcess::ExitStatus status);
- void on_stdErr();
- void on_stdOut();
+ void finish(int, QProcess::ExitStatus status);
+ void on_stdErr();
+ void on_stdOut();
};
View
6 libmultimc/src/instance.cpp
@@ -48,6 +48,12 @@ Instance::Instance(const QString &rootDir, QObject *parent) :
settings().registerSetting(new OverrideSetting("PostExitCommand",
globalSettings->getSetting("PostExitCommand")));
+ // Window Size
+ settings().registerSetting(new OverrideSetting("LaunchCompatMode", globalSettings->getSetting("LaunchCompatMode")));
+ settings().registerSetting(new OverrideSetting("LaunchMaximized", globalSettings->getSetting("LaunchMaximized")));
+ settings().registerSetting(new OverrideSetting("MinecraftWinWidth", globalSettings->getSetting("MinecraftWinWidth")));
+ settings().registerSetting(new OverrideSetting("MinecraftWinHeight", globalSettings->getSetting("MinecraftWinHeight")));
+
// Memory
settings().registerSetting(new OverrideSetting("MinMemAlloc", globalSettings->getSetting("MinMemAlloc")));
settings().registerSetting(new OverrideSetting("MaxMemAlloc", globalSettings->getSetting("MaxMemAlloc")));
View
160 libmultimc/src/instancelist.cpp
@@ -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
56 libmultimc/src/minecraftprocess.cpp
@@ -125,22 +125,41 @@ MinecraftProcess::MinecraftProcess(InstancePtr inst, QString user, QString sessi
// console window
void MinecraftProcess::on_stdErr()
{
-// if (m_console != nullptr)
-// m_console->write(readAllStandardError(), ConsoleWindow::ERROR);
+ QByteArray data = readAllStandardError();
+ QString str = m_err_leftover + QString::fromLocal8Bit(data);
+ m_err_leftover.clear();
+ QStringList lines = str.split("\n");
+ bool complete = str.endsWith("\n");
+
+ for(int i = 0; i < lines.size() - 1; i++)
+ {
+ QString & line = lines[i];
+ MessageLevel::Enum level = MessageLevel::Error;
+ if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") )
+ level = MessageLevel::Message;
+ if(line.contains("[SEVERE]") || line.contains("[WARNING]") || line.contains("[STDERR]"))
+ level = MessageLevel::Error;
+ emit log(lines[i].toLocal8Bit(), level);
+ }
+ if(!complete)
+ m_err_leftover = lines.last();
}
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));
+ QByteArray data = readAllStandardOutput();
+ QString str = m_out_leftover + QString::fromLocal8Bit(data);
+ m_out_leftover.clear();
+ QStringList lines = str.split("\n");
+ bool complete = str.endsWith("\n");
+
+ for(int i = 0; i < lines.size() - 1; i++)
+ {
+ QString & line = lines[i];
+ emit log(lines[i].toLocal8Bit(), MessageLevel::Message);
+ }
+ if(!complete)
+ m_out_leftover = lines.last();
}
// exit handler
@@ -151,7 +170,7 @@ void MinecraftProcess::finish(int code, ExitStatus status)
//TODO: error handling
}
- log("Minecraft exited.");
+ emit log("Minecraft exited.");
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
@@ -191,12 +210,15 @@ void MinecraftProcess::launch()
genArgs();
- log(QString("Minecraft folder is: '%1'").arg(workingDirectory()));
- log(QString("Instance launched with arguments: '%1'").arg(m_arguments.join("' '")));
-
- start(m_instance->settings().get("JavaPath").toString(), m_arguments);
+ emit log(QString("Minecraft folder is: '%1'").arg(workingDirectory()));
+ QString JavaPath = m_instance->settings().get("JavaPath").toString();
+ emit log(QString("Java path: '%1'").arg(JavaPath));
+ emit log(QString("Arguments: '%1'").arg(m_arguments.join("' '")));
+ start(JavaPath, m_arguments);
if (!waitForStarted())
{
+ emit log("Could not launch minecraft!");
+ return;
//TODO: error handling
}
View
11 libsettings/CMakeLists.txt
@@ -18,6 +18,12 @@ include/overridesetting.h
include/basicsettingsobject.h
include/inisettingsobject.h
+
+include/keyring.h
+)
+
+SET(LIBSETTINGS_HEADERS_PRIVATE
+src/stubkeyring.h
)
SET(LIBSETTINGS_SOURCES
@@ -29,6 +35,9 @@ src/overridesetting.cpp
src/basicsettingsobject.cpp
src/inisettingsobject.cpp
+
+src/keyring.cpp
+src/stubkeyring.cpp
)
# Set the include dir path.
@@ -37,6 +46,6 @@ include_directories(${LIBSETTINGS_INCLUDE_DIR})
add_definitions(-DLIBSETTINGS_LIBRARY)
-add_library(libSettings SHARED ${LIBSETTINGS_SOURCES} ${LIBSETTINGS_HEADERS})
+add_library(libSettings SHARED ${LIBSETTINGS_SOURCES} ${LIBSETTINGS_HEADERS} ${LIBSETTINGS_HEADERS_PRIVATE})
qt5_use_modules(libSettings Core)
target_link_libraries(libSettings)
View
92 libsettings/include/keyring.h
@@ -0,0 +1,92 @@
+/* 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 KEYRING_H
+#define KEYRING_H
+
+#include <QString>
+
+#include "libsettings_config.h"
+
+/**
+ * @file libsettings/include/keyring.h
+ * Access to System Keyrings
+ */
+
+/**
+ * @brief The Keyring class
+ * the System Keyring/Keychain/Wallet/Vault/etc
+ */
+class LIBSETTINGS_EXPORT Keyring
+{
+public:
+ /**
+ * @brief the System Keyring instance
+ * @return the Keyring instance
+ */
+ static Keyring *instance();
+