Merge branch 'master' of git://github.com/peterix/MultiMC5
Conflicts: CMakeLists.txt gui/mainwindow.cpp
This commit is contained in:
commit
ebb2c54975
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 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 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 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
|
||||
|
127
gui/iconcache.cpp
Normal file
127
gui/iconcache.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "iconcache.h"
|
||||
#include <QMap>
|
||||
#include <QWebView>
|
||||
#include <QWebFrame>
|
||||
#include <QEventLoop>
|
||||
#include <QWebElement>
|
||||
|
||||
IconCache* IconCache::m_Instance = 0;
|
||||
QMutex IconCache::mutex;
|
||||
#define MAX_SIZE 1024
|
||||
|
||||
class Private : public QWebView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QString name;
|
||||
QSize size;
|
||||
QMap<QString, QIcon> icons;
|
||||
|
||||
public:
|
||||
Private()
|
||||
{
|
||||
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(svgLoaded(bool)));
|
||||
setFixedSize(MAX_SIZE, MAX_SIZE);
|
||||
|
||||
QPalette pal = palette();
|
||||
pal.setColor(QPalette::Base, Qt::transparent);
|
||||
setPalette(pal);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||
size = QSize(128,128);
|
||||
}
|
||||
void renderSVGIcon(QString name);
|
||||
|
||||
signals:
|
||||
void svgRendered();
|
||||
|
||||
private slots:
|
||||
void svgLoaded(bool ok);
|
||||
};
|
||||
|
||||
void Private::svgLoaded(bool ok)
|
||||
{
|
||||
if (!ok)
|
||||
{
|
||||
emit svgRendered();
|
||||
return;
|
||||
}
|
||||
// check for SVG root tag
|
||||
QString root = page()->currentFrame()->documentElement().tagName();
|
||||
if (root.compare("svg", Qt::CaseInsensitive) != 0)
|
||||
{
|
||||
emit svgRendered();
|
||||
return;
|
||||
}
|
||||
|
||||
// get the size of the svg image, check if it's valid
|
||||
auto elem = page()->currentFrame()->documentElement();
|
||||
double width = elem.attribute("width").toDouble();
|
||||
double height = elem.attribute("height").toDouble();
|
||||
if (width == 0.0 || height == 0.0 || width == MAX_SIZE || height == MAX_SIZE)
|
||||
{
|
||||
emit svgRendered();
|
||||
return;
|
||||
}
|
||||
|
||||
// create the target surface
|
||||
QSize t = size.isValid() ? size : QSize(width, height);
|
||||
QImage img(t, QImage::Format_ARGB32_Premultiplied);
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
// prepare the painter, scale to required size
|
||||
QPainter p(&img);
|
||||
if(size.isValid())
|
||||
{
|
||||
p.scale(size.width() / width, size.height() / height);
|
||||
}
|
||||
|
||||
// the best quality
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setRenderHint(QPainter::TextAntialiasing);
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
|
||||
page()->mainFrame()->render(&p,QWebFrame::ContentsLayer);
|
||||
p.end();
|
||||
|
||||
icons[name] = QIcon(QPixmap::fromImage(img));
|
||||
emit svgRendered();
|
||||
}
|
||||
|
||||
void Private::renderSVGIcon ( QString name )
|
||||
{
|
||||
// use event loop to wait for signal
|
||||
QEventLoop loop;
|
||||
this->name = name;
|
||||
QString prefix = "qrc:/icons/instances/";
|
||||
QObject::connect(this, SIGNAL(svgRendered()), &loop, SLOT(quit()));
|
||||
load(QUrl(prefix + name));
|
||||
loop.exec();
|
||||
}
|
||||
|
||||
IconCache::IconCache():d(new Private())
|
||||
{
|
||||
}
|
||||
|
||||
QIcon IconCache::getIcon ( QString name )
|
||||
{
|
||||
if(name == "default")
|
||||
name = "infinity";
|
||||
{
|
||||
auto iter = d->icons.find(name);
|
||||
if(iter != d->icons.end())
|
||||
return *iter;
|
||||
}
|
||||
d->renderSVGIcon(name);
|
||||
auto iter = d->icons.find(name);
|
||||
if(iter != d->icons.end())
|
||||
return *iter;
|
||||
|
||||
// Fallback for icons that don't exist.
|
||||
QString path = ":/icons/instances/infinity";
|
||||
//path += name;
|
||||
d->icons[name] = QIcon(path);
|
||||
return d->icons[name];
|
||||
}
|
||||
|
||||
#include "iconcache.moc"
|
43
gui/iconcache.h
Normal file
43
gui/iconcache.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMutex>
|
||||
#include <QtGui/QIcon>
|
||||
|
||||
class Private;
|
||||
|
||||
class IconCache
|
||||
{
|
||||
public:
|
||||
static IconCache* instance()
|
||||
{
|
||||
if (!m_Instance)
|
||||
{
|
||||
mutex.lock();
|
||||
if (!m_Instance)
|
||||
m_Instance = new IconCache;
|
||||
mutex.unlock();
|
||||
}
|
||||
return m_Instance;
|
||||
}
|
||||
|
||||
static void drop()
|
||||
{
|
||||
mutex.lock();
|
||||
delete m_Instance;
|
||||
m_Instance = 0;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
QIcon getIcon(QString name);
|
||||
|
||||
private:
|
||||
IconCache();
|
||||
// hide copy constructor
|
||||
IconCache(const IconCache &);
|
||||
// hide assign op
|
||||
IconCache& operator=(const IconCache &);
|
||||
static IconCache* m_Instance;
|
||||
static QMutex mutex;
|
||||
Private* d;
|
||||
};
|
||||
|
@ -33,9 +33,15 @@ ListViewDelegate::ListViewDelegate ( QObject* parent ) : QStyledItemDelegate ( p
|
||||
|
||||
void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
|
||||
{
|
||||
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)
|
||||
|
@ -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;
|
||||
|
@ -22,9 +22,14 @@ public:
|
||||
QVariant data ( const QModelIndex& index, int role ) const;
|
||||
Qt::ItemFlags flags ( const QModelIndex& index ) const;
|
||||
|
||||
public slots:
|
||||
void onInstanceAdded(int index);
|
||||
void onInstanceChanged(int index);
|
||||
void onInvalidated();
|
||||
|
||||
private:
|
||||
const InstanceList* m_instances;
|
||||
QIcon cachedIcon;
|
||||
int currentInstancesNumber;
|
||||
};
|
||||
|
||||
class InstanceProxyModel : public KCategorizedSortFilterProxyModel
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -32,7 +32,11 @@ public:
|
||||
|
||||
QString getUsername() const;
|
||||
QString getPassword() const;
|
||||
|
||||
|
||||
public slots:
|
||||
virtual void accept();
|
||||
virtual void userTextChanged(const QString& user);
|
||||
virtual void forgetCurrentUser();
|
||||
private:
|
||||
Ui::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>
|
||||
<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="text">
|
||||
<string>Forget</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="checkboxLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="forceUpdateButton">
|
||||
<property name="text">
|
||||
<string>&Force Update</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="rememberUsernameCheckbox">
|
||||
<property name="sizePolicy">
|
||||
|
@ -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 )
|
||||
|
@ -26,6 +26,8 @@ class InstanceModel;
|
||||
class InstanceProxyModel;
|
||||
class KCategorizedView;
|
||||
class KCategoryDrawer;
|
||||
class MinecraftProcess;
|
||||
class ConsoleWindow;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@ -44,14 +46,19 @@ public:
|
||||
|
||||
// Browser Dialog
|
||||
void openWebPage(QUrl url);
|
||||
|
||||
private:
|
||||
Instance *selectedInstance();
|
||||
|
||||
private slots:
|
||||
void on_actionAbout_triggered();
|
||||
|
||||
void on_actionAddInstance_triggered();
|
||||
|
||||
void on_actionViewInstanceFolder_triggered();
|
||||
void on_actionChangeInstGroup_triggered();
|
||||
|
||||
void on_actionViewInstanceFolder_triggered();
|
||||
|
||||
void on_actionRefresh_triggered();
|
||||
|
||||
void on_actionViewCentralModsFolder_triggered();
|
||||
@ -91,6 +98,8 @@ private:
|
||||
InstanceModel * model;
|
||||
InstanceProxyModel * proxymodel;
|
||||
InstanceList instList;
|
||||
MinecraftProcess *proc;
|
||||
ConsoleWindow *console;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
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);
|
||||
QString version = MCVer_Unknown;
|
||||
|
||||
// open the zip
|
||||
wxFFileInputStream inStream(jar.GetFullPath());
|
||||
wxZipInputStream zipIn(inStream);
|
||||
|
||||
// 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)
|
||||
// check if minecraft.jar exists
|
||||
QFile jar(jarName);
|
||||
if (!jar.exists())
|
||||
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())
|
||||
|
||||
// open minecraft.jar
|
||||
QuaZip zip(&jar);
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
return version;
|
||||
|
||||
// open Minecraft.class
|
||||
zip.setCurrentFile("net/minecraft/client/Minecraft.class", QuaZip::csSensitive);
|
||||
QuaZipFile Minecraft(&zip);
|
||||
if (!Minecraft.open(QuaZipFile::ReadOnly))
|
||||
return version;
|
||||
|
||||
// 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;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -208,6 +208,16 @@ public:
|
||||
*/
|
||||
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.
|
||||
|
@ -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 )
|
||||
|
@ -32,126 +32,128 @@ class KCategoryDrawerV3;
|
||||
class KCategorizedView::Private
|
||||
{
|
||||
public:
|
||||
struct Block;
|
||||
struct Item;
|
||||
struct Block;
|
||||
struct Item;
|
||||
|
||||
Private(KCategorizedView *q);
|
||||
~Private();
|
||||
Private(KCategorizedView *q);
|
||||
~Private();
|
||||
|
||||
/**
|
||||
* @return whether this view has all required elements to be categorized.
|
||||
*/
|
||||
bool isCategorized() const;
|
||||
/**
|
||||
* @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);
|
||||
/**
|
||||
* @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 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 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 height of the block determined by @p category.
|
||||
*/
|
||||
int blockHeight(const QString &category);
|
||||
|
||||
/**
|
||||
* Returns the actual viewport width.
|
||||
*/
|
||||
int viewportWidth() const;
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* 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 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 @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 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 whether the view has a valid grid size.
|
||||
*/
|
||||
bool hasGrid() const;
|
||||
|
||||
/**
|
||||
* Returns the category for the given index.
|
||||
*/
|
||||
QString categoryForIndex(const QModelIndex &index) 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 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;
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* 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;
|
||||
KCategorizedView *q;
|
||||
KCategorizedSortFilterProxyModel *proxyModel;
|
||||
KCategoryDrawer *categoryDrawer;
|
||||
int categorySpacing;
|
||||
bool alternatingBlockColors;
|
||||
bool collapsibleBlocks;
|
||||
bool constantItemWidth;
|
||||
|
||||
Block *hoveredBlock;
|
||||
QString hoveredCategory;
|
||||
QModelIndex hoveredIndex;
|
||||
Block *hoveredBlock;
|
||||
QString hoveredCategory;
|
||||
QModelIndex hoveredIndex;
|
||||
|
||||
QPoint pressedPosition;
|
||||
QRect rubberBandRect;
|
||||
QPoint pressedPosition;
|
||||
QRect rubberBandRect;
|
||||
|
||||
QHash<QString, Block> blocks;
|
||||
QHash<QString, Block> blocks;
|
||||
};
|
||||
|
||||
#endif // KCATEGORIZEDVIEW_P_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 @@ public:
|
||||
|
||||
//// General Info ////
|
||||
virtual QString name() { return settings().get("name").toString(); }
|
||||
virtual void setName(QString val) { settings().set("name", val); }
|
||||
virtual void setName(QString val)
|
||||
{
|
||||
settings().set("name", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
virtual QString iconKey() const { return settings().get("iconKey").toString(); }
|
||||
virtual void setIconKey(QString val) { settings().set("iconKey", val); }
|
||||
virtual void setIconKey(QString val)
|
||||
{
|
||||
settings().set("iconKey", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
virtual QString notes() const { return settings().get("notes").toString(); }
|
||||
virtual void setNotes(QString val) { settings().set("notes", val); }
|
||||
|
||||
virtual QString group() const { return m_group; }
|
||||
virtual void setGroup(QString val)
|
||||
{
|
||||
m_group = val;
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
virtual bool shouldRebuild() const { return settings().get("NeedsRebuild").toBool(); }
|
||||
virtual void setShouldRebuild(bool val) { settings().set("NeedsRebuild", val); }
|
||||
|
||||
@ -202,7 +220,10 @@ public:
|
||||
|
||||
virtual qint64 lastLaunch() { return settings().get("lastLaunchTime").value<qint64>(); }
|
||||
virtual void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch())
|
||||
{ settings().set("lastLaunchTime", val); }
|
||||
{
|
||||
settings().set("lastLaunchTime", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
|
||||
////// Directories //////
|
||||
@ -277,8 +298,15 @@ public:
|
||||
*/
|
||||
virtual SettingsObject &settings() const;
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Signal emitted when properties relevant to the instance view change
|
||||
*/
|
||||
void propertiesChanged(Instance * inst);
|
||||
|
||||
private:
|
||||
QString m_rootDir;
|
||||
QString m_group;
|
||||
SettingsObject *m_settings;
|
||||
};
|
||||
|
||||
|
@ -17,16 +17,14 @@
|
||||
#define INSTANCELIST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "siglist.h"
|
||||
|
||||
#include "instance.h"
|
||||
#include "libmmc_config.h"
|
||||
|
||||
class Instance;
|
||||
|
||||
class LIBMULTIMC_EXPORT InstanceList : public QObject, public SigList< QSharedPointer<Instance> >
|
||||
class LIBMULTIMC_EXPORT InstanceList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -46,14 +44,46 @@ public:
|
||||
QString instDir() const { return m_instDir; }
|
||||
|
||||
/*!
|
||||
* \brief Loads the instance list.
|
||||
* \brief Loads the instance list. Triggers notifications.
|
||||
*/
|
||||
InstListError loadList();
|
||||
|
||||
DEFINE_SIGLIST_SIGNALS(QSharedPointer<Instance>);
|
||||
SETUP_SIGLIST_SIGNALS(QSharedPointer<Instance>);
|
||||
/*!
|
||||
* \brief Get the instance at index
|
||||
*/
|
||||
InstancePtr at(int i) const
|
||||
{
|
||||
return m_instances.at(i);
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Get the count of loaded instances
|
||||
*/
|
||||
int count() const
|
||||
{
|
||||
return m_instances.count();
|
||||
};
|
||||
|
||||
/// Clear all instances. Triggers notifications.
|
||||
void clear();
|
||||
|
||||
/// Add an instance. Triggers notifications, returns the new index
|
||||
int add(InstancePtr t);
|
||||
|
||||
/// Get an instance by ID
|
||||
InstancePtr getInstanceById (QString id);
|
||||
|
||||
signals:
|
||||
void instanceAdded(int index);
|
||||
void instanceChanged(int index);
|
||||
void invalidated();
|
||||
|
||||
private slots:
|
||||
void propertiesChanged(Instance * inst);
|
||||
|
||||
protected:
|
||||
QString m_instDir;
|
||||
QList< InstancePtr > m_instances;
|
||||
};
|
||||
|
||||
#endif // INSTANCELIST_H
|
||||
|
@ -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,
|
||||
@ -23,74 +23,98 @@
|
||||
|
||||
#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 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 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 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 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 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 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();
|
||||
|
||||
};
|
||||
|
||||
|
@ -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")));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
92
libsettings/include/keyring.h
Normal file
92
libsettings/include/keyring.h
Normal file
@ -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();
|
||||
|
||||
/**
|
||||
* @brief store a password in the Keyring
|
||||
* @param service the service name
|
||||
* @param username the account name
|
||||
* @param password the password to store
|
||||
* @return success
|
||||
*/
|
||||
virtual bool storePassword(QString service, QString username, QString password) = 0;
|
||||
|
||||
/**
|
||||
* @brief get a password from the Keyring
|
||||
* @param service the service name
|
||||
* @param username the account name
|
||||
* @return the password (success=!isNull())
|
||||
*/
|
||||
virtual QString getPassword(QString service, QString username) = 0;
|
||||
|
||||
/**
|
||||
* @brief lookup a password
|
||||
* @param service the service name
|
||||
* @param username the account name
|
||||
* @return wether the password is available
|
||||
*/
|
||||
virtual bool hasPassword(QString service, QString username) = 0;
|
||||
|
||||
/**
|
||||
* @brief get a list of all stored accounts.
|
||||
* @param service the service name
|
||||
* @return
|
||||
*/
|
||||
virtual QStringList getStoredAccounts(QString service) = 0;
|
||||
|
||||
/**
|
||||
* @brief Remove the specified account from storage
|
||||
* @param service the service name
|
||||
* @param username the account name
|
||||
* @return
|
||||
*/
|
||||
virtual void removeStoredAccount(QString service, QString username) = 0;
|
||||
|
||||
protected:
|
||||
/// fall back to StubKeyring if false
|
||||
virtual bool isValid() { return false; }
|
||||
|
||||
private:
|
||||
static Keyring *m_instance;
|
||||
static void destroy();
|
||||
};
|
||||
|
||||
#endif // KEYRING_H
|
@ -26,7 +26,10 @@ void BasicSettingsObject::changeSetting(const Setting &setting, QVariant value)
|
||||
{
|
||||
if (contains(setting.id()))
|
||||
{
|
||||
config.setValue(setting.configKey(), value);
|
||||
if(value.isValid())
|
||||
config.setValue(setting.configKey(), value);
|
||||
else
|
||||
config.remove(setting.configKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,10 @@ void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
|
||||
{
|
||||
if (contains(setting.id()))
|
||||
{
|
||||
m_ini.set(setting.configKey(), value);
|
||||
if(value.isValid())
|
||||
m_ini.set(setting.configKey(), value);
|
||||
else
|
||||
m_ini.remove(setting.configKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
63
libsettings/src/keyring.cpp
Normal file
63
libsettings/src/keyring.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/* 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/keyring.h"
|
||||
|
||||
#include "osutils.h"
|
||||
|
||||
#include "stubkeyring.h"
|
||||
|
||||
// system specific keyrings
|
||||
/*#if defined(OSX)
|
||||
class OSXKeychain;
|
||||
#define KEYRING OSXKeychain
|
||||
#elif defined(LINUX)
|
||||
class XDGKeyring;
|
||||
#define KEYRING XDGKeyring
|
||||
#elif defined(WINDOWS)
|
||||
class Win32Keystore;
|
||||
#define KEYRING Win32Keystore
|
||||
#else
|
||||
#pragma message Keyrings are not supported on your os. Falling back to the insecure StubKeyring
|
||||
#endif*/
|
||||
|
||||
Keyring *Keyring::instance()
|
||||
{
|
||||
if (m_instance == nullptr)
|
||||
{
|
||||
#ifdef KEYRING
|
||||
m_instance = new KEYRING();
|
||||
if (!m_instance->isValid())
|
||||
{
|
||||
qWarning("Could not create SystemKeyring! falling back to StubKeyring.");
|
||||
m_instance = new StubKeyring();
|
||||
}
|
||||
#else
|
||||
qWarning("Keyrings are not supported on your OS. Fallback StubKeyring is insecure!");
|
||||
m_instance = new StubKeyring();
|
||||
#endif
|
||||
atexit(Keyring::destroy);
|
||||
}
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void Keyring::destroy()
|
||||
{
|
||||
delete m_instance;
|
||||
}
|
||||
|
||||
Keyring *Keyring::m_instance;
|
@ -26,9 +26,16 @@ QVariant Setting::get() const
|
||||
{
|
||||
SettingsObject *sbase = qobject_cast<SettingsObject *>(parent());
|
||||
if (!sbase)
|
||||
{
|
||||
return defValue();
|
||||
}
|
||||
else
|
||||
return sbase->retrieveValue(*this);
|
||||
{
|
||||
QVariant test = sbase->retrieveValue(*this);
|
||||
if(!test.isValid())
|
||||
return defValue();
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Setting::defValue() const
|
||||
|
104
libsettings/src/stubkeyring.cpp
Normal file
104
libsettings/src/stubkeyring.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/* 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 "stubkeyring.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
// Scrambling
|
||||
// this is NOT SAFE, but it's not plain either.
|
||||
int scrambler = 0x9586309;
|
||||
|
||||
QString scramble(QString in_)
|
||||
{
|
||||
QByteArray in = in_.toUtf8();
|
||||
QByteArray out;
|
||||
for (int i = 0; i<in.length(); i++)
|
||||
out.append(in.at(i) ^ scrambler);
|
||||
return QString::fromUtf8(out);
|
||||
}
|
||||
|
||||
inline QString base64(QString in)
|
||||
{
|
||||
return QString(in.toUtf8().toBase64());
|
||||
}
|
||||
inline QString unbase64(QString in)
|
||||
{
|
||||
return QString::fromUtf8(QByteArray::fromBase64(in.toLatin1()));
|
||||
}
|
||||
|
||||
inline QString scramble64(QString in)
|
||||
{
|
||||
return base64(scramble(in));
|
||||
}
|
||||
inline QString unscramble64(QString in)
|
||||
{
|
||||
return scramble(unbase64(in));
|
||||
}
|
||||
|
||||
// StubKeyring implementation
|
||||
inline QString generateKey(QString service, QString username)
|
||||
{
|
||||
return QString("%1/%2").arg(base64(service)).arg(scramble64(username));
|
||||
}
|
||||
|
||||
bool StubKeyring::storePassword(QString service, QString username, QString password)
|
||||
{
|
||||
m_settings.setValue(generateKey(service, username), scramble64(password));
|
||||
return true;
|
||||
}
|
||||
|
||||
QString StubKeyring::getPassword(QString service, QString username)
|
||||
{
|
||||
QString key = generateKey(service, username);
|
||||
if (!m_settings.contains(key))
|
||||
return QString();
|
||||
return unscramble64(m_settings.value(key).toString());
|
||||
}
|
||||
|
||||
bool StubKeyring::hasPassword(QString service, QString username)
|
||||
{
|
||||
return m_settings.contains(generateKey(service, username));
|
||||
}
|
||||
|
||||
QStringList StubKeyring::getStoredAccounts(QString service)
|
||||
{
|
||||
service = base64(service).append('/');
|
||||
QStringList out;
|
||||
QStringList in(m_settings.allKeys());
|
||||
QStringListIterator it(in);
|
||||
while(it.hasNext())
|
||||
{
|
||||
QString c = it.next();
|
||||
if (c.startsWith(service))
|
||||
out << unscramble64(c.mid(service.length()));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void StubKeyring::removeStoredAccount ( QString service, QString username )
|
||||
{
|
||||
QString key = generateKey(service, username);
|
||||
m_settings.remove(key);
|
||||
}
|
||||
|
||||
//FIXME: this needs tweaking/changes for user account level storage
|
||||
StubKeyring::StubKeyring() :
|
||||
// m_settings(QSettings::UserScope, "Orochimarufan", "Keyring")
|
||||
m_settings("keyring.cfg", QSettings::IniFormat)
|
||||
{
|
||||
}
|
41
libsettings/src/stubkeyring.h
Normal file
41
libsettings/src/stubkeyring.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* 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 STUBKEYRING_H
|
||||
#define STUBKEYRING_H
|
||||
|
||||
#include "include/keyring.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
class StubKeyring : public Keyring
|
||||
{
|
||||
public:
|
||||
virtual bool storePassword(QString service, QString username, QString password);
|
||||
virtual QString getPassword(QString service, QString username);
|
||||
virtual bool hasPassword(QString service, QString username);
|
||||
virtual QStringList getStoredAccounts(QString service);
|
||||
virtual void removeStoredAccount(QString service, QString username);
|
||||
private:
|
||||
friend class Keyring;
|
||||
explicit StubKeyring();
|
||||
virtual bool isValid() { return true; }
|
||||
|
||||
QSettings m_settings;
|
||||
};
|
||||
|
||||
#endif // STUBKEYRING_H
|
@ -31,9 +31,6 @@ include/pathutils.h
|
||||
include/osutils.h
|
||||
include/userutils.h
|
||||
include/cmdutils.h
|
||||
|
||||
include/siglist.h
|
||||
include/siglist_impl.h
|
||||
)
|
||||
|
||||
SET(LIBUTIL_SOURCES
|
||||
|
@ -73,15 +73,6 @@ enum LIBUTIL_EXPORT Enum
|
||||
};
|
||||
}
|
||||
|
||||
namespace OptionType
|
||||
{
|
||||
enum LIBUTIL_EXPORT Enum
|
||||
{
|
||||
Switch,
|
||||
Option
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ParsingError class
|
||||
*/
|
||||
@ -210,6 +201,12 @@ public:
|
||||
private:
|
||||
FlagStyle::Enum m_flagStyle;
|
||||
ArgumentStyle::Enum m_argStyle;
|
||||
|
||||
enum OptionType
|
||||
{
|
||||
otSwitch,
|
||||
otOption
|
||||
};
|
||||
|
||||
// Important: the common part MUST BE COMMON ON ALL THREE structs
|
||||
struct CommonDef {
|
||||
@ -226,7 +223,7 @@ private:
|
||||
QString metavar;
|
||||
QVariant def;
|
||||
// option
|
||||
OptionType::Enum type;
|
||||
OptionType type;
|
||||
QChar flag;
|
||||
};
|
||||
|
||||
|
@ -56,7 +56,7 @@ void Parser::addSwitch(QString name, bool def)
|
||||
throw "Name not unique";
|
||||
|
||||
OptionDef *param = new OptionDef;
|
||||
param->type = OptionType::Switch;
|
||||
param->type = otSwitch;
|
||||
param->name = name;
|
||||
param->metavar = QString("<%1>").arg(name);
|
||||
param->def = def;
|
||||
@ -72,7 +72,7 @@ void Parser::addOption(QString name, QVariant def)
|
||||
throw "Name not unique";
|
||||
|
||||
OptionDef *param = new OptionDef;
|
||||
param->type = OptionType::Option;
|
||||
param->type = otOption;
|
||||
param->name = name;
|
||||
param->metavar = QString("<%1>").arg(name);
|
||||
param->def = def;
|
||||
@ -161,7 +161,7 @@ QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
|
||||
help << flagPrefix << option->flag << ", ";
|
||||
}
|
||||
help << optPrefix << option->name;
|
||||
if (option->type == OptionType::Option)
|
||||
if (option->type == otOption)
|
||||
{
|
||||
QString arg = QString("%1%2").arg(((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
|
||||
nameLength += arg.length();
|
||||
@ -193,7 +193,7 @@ QString Parser::compileUsage(QString progName, bool useFlags)
|
||||
usage << flagPrefix << option->flag;
|
||||
else
|
||||
usage << optPrefix << option->name;
|
||||
if (option->type == OptionType::Option)
|
||||
if (option->type == otOption)
|
||||
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
|
||||
usage << "]";
|
||||
}
|
||||
@ -265,9 +265,9 @@ QHash<QString, QVariant> Parser::parse(QStringList argv)
|
||||
throw ParsingError(QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
|
||||
|
||||
OptionDef *option = m_options[name];
|
||||
if (option->type == OptionType::Switch)
|
||||
if (option->type == otSwitch)
|
||||
map[name] = true;
|
||||
else //if (option->type == OptionType::Option)
|
||||
else //if (option->type == otOption)
|
||||
{
|
||||
if (m_argStyle == ArgumentStyle::Space)
|
||||
expecting.append(name);
|
||||
@ -312,9 +312,9 @@ QHash<QString, QVariant> Parser::parse(QStringList argv)
|
||||
if (map.contains(option->name))
|
||||
throw ParsingError(QString("Option %2%1 was given multiple times").arg(option->name, optionPrefix));
|
||||
|
||||
if (option->type == OptionType::Switch)
|
||||
if (option->type == otSwitch)
|
||||
map[option->name] = true;
|
||||
else //if (option->type == OptionType::Option)
|
||||
else //if (option->type == otOption)
|
||||
{
|
||||
if (m_argStyle == ArgumentStyle::Space)
|
||||
expecting.append(option->name);
|
||||
|
20
main.cpp
20
main.cpp
@ -57,23 +57,6 @@ public:
|
||||
this->instId = instId;
|
||||
}
|
||||
|
||||
private:
|
||||
InstancePtr findInstance(QString instId)
|
||||
{
|
||||
QListIterator<InstancePtr> iter(instances);
|
||||
InstancePtr inst;
|
||||
while(iter.hasNext())
|
||||
{
|
||||
inst = iter.next();
|
||||
if (inst->id() == instId)
|
||||
break;
|
||||
}
|
||||
if (inst->id() != instId)
|
||||
return InstancePtr();
|
||||
else
|
||||
return iter.peekPrevious();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onTerminated()
|
||||
{
|
||||
@ -89,6 +72,7 @@ private slots:
|
||||
//if (instance->getShowConsole())
|
||||
console->show();
|
||||
connect(proc, SIGNAL(ended()), SLOT(onTerminated()));
|
||||
connect(proc, SIGNAL(log(QString,MessageLevel::Enum)), console, SLOT(write(QString,MessageLevel::Enum)));
|
||||
proc->launch();
|
||||
}
|
||||
|
||||
@ -117,7 +101,7 @@ public:
|
||||
instances.loadList();
|
||||
|
||||
std::cout << "Launching Instance '" << qPrintable(instId) << "'" << std::endl;
|
||||
instance = findInstance(instId);
|
||||
instance = instances.getInstanceById(instId);
|
||||
if (instance.isNull())
|
||||
{
|
||||
std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl;
|
||||
|
@ -34,4 +34,7 @@
|
||||
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
|
||||
<file alias="index.theme">resources/XdgIcon.theme</file>
|
||||
</qresource>
|
||||
<qresource prefix="/backgrounds">
|
||||
<file alias="kitteh">resources/catbgrnd2.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
BIN
resources/catbgrnd2.png
Normal file
BIN
resources/catbgrnd2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
@ -14,7 +14,7 @@
|
||||
height="32"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="clucker.svg"
|
||||
inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/chicken128.png"
|
||||
inkscape:export-xdpi="360"
|
||||
@ -234,13 +234,13 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="-92.12757"
|
||||
inkscape:cy="71.871222"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="2.6058272"
|
||||
inkscape:cy="11.408405"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1607"
|
||||
inkscape:window-width="1614"
|
||||
inkscape:window-height="1030"
|
||||
inkscape:window-x="1676"
|
||||
inkscape:window-y="-3"
|
||||
@ -261,7 +261,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -13,26 +13,13 @@
|
||||
height="32"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="skeleton.svg"
|
||||
inkscape:export-filename="/home/peterix/projects/MultiMC4/src/resources/insticons/skeleton128.png"
|
||||
inkscape:export-xdpi="360"
|
||||
inkscape:export-ydpi="360">
|
||||
<defs
|
||||
id="defs4">
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter5719"
|
||||
x="-0.18000001"
|
||||
width="1.36"
|
||||
y="-0.36000001"
|
||||
height="1.72">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.15"
|
||||
id="feGaussianBlur5721" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
@ -46,19 +33,6 @@
|
||||
stdDeviation="0.35"
|
||||
id="feGaussianBlur5725" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
id="filter5711"
|
||||
x="-0.1728"
|
||||
width="1.3456"
|
||||
y="-0.34560001"
|
||||
height="1.6912">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.144"
|
||||
id="feGaussianBlur5713" />
|
||||
</filter>
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
inkscape:collect="always"
|
||||
@ -103,12 +77,12 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="17.044214"
|
||||
inkscape:cy="19.500236"
|
||||
inkscape:cx="-18.309169"
|
||||
inkscape:cy="22.958832"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1607"
|
||||
inkscape:window-width="1614"
|
||||
inkscape:window-height="1030"
|
||||
inkscape:window-x="1676"
|
||||
inkscape:window-y="-3"
|
||||
@ -129,7 +103,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@ -612,7 +586,7 @@
|
||||
x="7.9999995"
|
||||
y="1036.3622" />
|
||||
<rect
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5711)"
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:none;"
|
||||
id="rect4084-3"
|
||||
width="2"
|
||||
height="1"
|
||||
@ -626,7 +600,7 @@
|
||||
x="20"
|
||||
y="1036.3622" />
|
||||
<rect
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:none;filter:url(#filter5719)"
|
||||
style="fill:#00ffff;fill-opacity:1;stroke:none;"
|
||||
id="rect4086-7"
|
||||
width="2"
|
||||
height="1"
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
60
test.cpp
Normal file
60
test.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "keyring.h"
|
||||
#include "cmdutils.h"
|
||||
|
||||
using namespace Util::Commandline;
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName("MMC Keyring test");
|
||||
app.setOrganizationName("Orochimarufan");
|
||||
|
||||
Parser p;
|
||||
p.addArgument("user", false);
|
||||
p.addArgument("password", false);
|
||||
p.addSwitch("set");
|
||||
p.addSwitch("get");
|
||||
p.addSwitch("list");
|
||||
p.addOption("service", "Test");
|
||||
p.addShortOpt("service", 's');
|
||||
|
||||
QHash<QString, QVariant> args;
|
||||
try {
|
||||
args = p.parse(app.arguments());
|
||||
} catch (ParsingError) {
|
||||
std::cout << "Syntax error." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args["set"].toBool()) {
|
||||
if (args["user"].isNull() || args["password"].isNull()) {
|
||||
std::cout << "set operation needs bot user and password set" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Keyring::instance()->storePassword(args["service"].toString(),
|
||||
args["user"].toString(), args["password"].toString());
|
||||
} else if (args["get"].toBool()) {
|
||||
if (args["user"].isNull()) {
|
||||
std::cout << "get operation needs user set" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Password: " << qPrintable(Keyring::instance()->getPassword(args["service"].toString(),
|
||||
args["user"].toString())) << std::endl;
|
||||
return 0;
|
||||
} else if (args["list"].toBool()) {
|
||||
QStringList accounts = Keyring::instance()->getStoredAccounts(args["service"].toString());
|
||||
std::cout << "stored accounts:" << std::endl << '\t' << qPrintable(accounts.join("\n\t")) << std::endl;
|
||||
return 0;
|
||||
} else {
|
||||
std::cout << "No operation given!" << std::endl;
|
||||
std::cout << qPrintable(p.compileHelp(argv[0])) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user