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

Conflicts:
	CMakeLists.txt
	gui/mainwindow.cpp
This commit is contained in:
Andrew 2013-03-28 11:37:12 -05:00
commit ebb2c54975
43 changed files with 1475 additions and 461 deletions

View File

@ -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 File

@ -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 File

@ -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
View File

@ -0,0 +1,127 @@
#include "iconcache.h"
#include <QMap>
#include <QWebView>
#include <QWebFrame>
#include <QEventLoop>
#include <QWebElement>
IconCache* IconCache::m_Instance = 0;
QMutex IconCache::mutex;
#define MAX_SIZE 1024
class Private : public QWebView
{
Q_OBJECT
public:
QString name;
QSize size;
QMap<QString, QIcon> icons;
public:
Private()
{
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(svgLoaded(bool)));
setFixedSize(MAX_SIZE, MAX_SIZE);
QPalette pal = palette();
pal.setColor(QPalette::Base, Qt::transparent);
setPalette(pal);
setAttribute(Qt::WA_OpaquePaintEvent, false);
size = QSize(128,128);
}
void renderSVGIcon(QString name);
signals:
void svgRendered();
private slots:
void svgLoaded(bool ok);
};
void Private::svgLoaded(bool ok)
{
if (!ok)
{
emit svgRendered();
return;
}
// check for SVG root tag
QString root = page()->currentFrame()->documentElement().tagName();
if (root.compare("svg", Qt::CaseInsensitive) != 0)
{
emit svgRendered();
return;
}
// get the size of the svg image, check if it's valid
auto elem = page()->currentFrame()->documentElement();
double width = elem.attribute("width").toDouble();
double height = elem.attribute("height").toDouble();
if (width == 0.0 || height == 0.0 || width == MAX_SIZE || height == MAX_SIZE)
{
emit svgRendered();
return;
}
// create the target surface
QSize t = size.isValid() ? size : QSize(width, height);
QImage img(t, QImage::Format_ARGB32_Premultiplied);
img.fill(Qt::transparent);
// prepare the painter, scale to required size
QPainter p(&img);
if(size.isValid())
{
p.scale(size.width() / width, size.height() / height);
}
// the best quality
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::TextAntialiasing);
p.setRenderHint(QPainter::SmoothPixmapTransform);
page()->mainFrame()->render(&p,QWebFrame::ContentsLayer);
p.end();
icons[name] = QIcon(QPixmap::fromImage(img));
emit svgRendered();
}
void Private::renderSVGIcon ( QString name )
{
// use event loop to wait for signal
QEventLoop loop;
this->name = name;
QString prefix = "qrc:/icons/instances/";
QObject::connect(this, SIGNAL(svgRendered()), &loop, SLOT(quit()));
load(QUrl(prefix + name));
loop.exec();
}
IconCache::IconCache():d(new Private())
{
}
QIcon IconCache::getIcon ( QString name )
{
if(name == "default")
name = "infinity";
{
auto iter = d->icons.find(name);
if(iter != d->icons.end())
return *iter;
}
d->renderSVGIcon(name);
auto iter = d->icons.find(name);
if(iter != d->icons.end())
return *iter;
// Fallback for icons that don't exist.
QString path = ":/icons/instances/infinity";
//path += name;
d->icons[name] = QIcon(path);
return d->icons[name];
}
#include "iconcache.moc"

43
gui/iconcache.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <QMutex>
#include <QtGui/QIcon>
class Private;
class IconCache
{
public:
static IconCache* instance()
{
if (!m_Instance)
{
mutex.lock();
if (!m_Instance)
m_Instance = new IconCache;
mutex.unlock();
}
return m_Instance;
}
static void drop()
{
mutex.lock();
delete m_Instance;
m_Instance = 0;
mutex.unlock();
}
QIcon getIcon(QString name);
private:
IconCache();
// hide copy constructor
IconCache(const IconCache &);
// hide assign op
IconCache& operator=(const IconCache &);
static IconCache* m_Instance;
static QMutex mutex;
Private* d;
};

View File

@ -33,9 +33,15 @@ ListViewDelegate::ListViewDelegate ( QObject* parent ) : QStyledItemDelegate ( p
void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)
{
if (!(option.state & QStyle::State_Selected))
return;
painter->fillRect ( rect, option.palette.brush ( QPalette::Highlight ) );
if ((option.state & QStyle::State_Selected))
painter->fillRect ( rect, option.palette.brush ( QPalette::Highlight ) );
else
{
QColor backgroundColor = option.palette.color(QPalette::Background);
backgroundColor.setAlpha(160);
painter->fillRect ( rect, QBrush(backgroundColor) );
}
}
void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect)

View File

@ -1,13 +1,38 @@
#include "instancemodel.h"
#include <instance.h>
#include <QIcon>
#include "iconcache.h"
InstanceModel::InstanceModel ( const InstanceList& instances, QObject *parent )
: QAbstractListModel ( parent ), m_instances ( &instances )
{
cachedIcon = QIcon(":/icons/multimc/scalable/apps/multimc.svg");
currentInstancesNumber = m_instances->count();
connect(m_instances,SIGNAL(instanceAdded(int)),this,SLOT(onInstanceAdded(int)));
connect(m_instances,SIGNAL(instanceChanged(int)),this,SLOT(onInstanceChanged(int)));
connect(m_instances,SIGNAL(invalidated()),this,SLOT(onInvalidated()));
}
void InstanceModel::onInstanceAdded ( int index )
{
beginInsertRows(QModelIndex(), index, index);
currentInstancesNumber ++;
endInsertRows();
}
void InstanceModel::onInstanceChanged ( int index )
{
QModelIndex mx = InstanceModel::index(index);
dataChanged(mx,mx);
}
void InstanceModel::onInvalidated()
{
beginResetModel();
currentInstancesNumber = m_instances->count();
endResetModel();
}
int InstanceModel::rowCount ( const QModelIndex& parent ) const
{
Q_UNUSED ( parent );
@ -17,7 +42,7 @@ int InstanceModel::rowCount ( const QModelIndex& parent ) const
QModelIndex InstanceModel::index ( int row, int column, const QModelIndex& parent ) const
{
Q_UNUSED ( parent );
if ( row < 0 || row >= m_instances->count() )
if ( row < 0 || row >= currentInstancesNumber )
return QModelIndex();
return createIndex ( row, column, ( void* ) m_instances->at ( row ).data() );
}
@ -46,14 +71,22 @@ QVariant InstanceModel::data ( const QModelIndex& index, int role ) const
}
case Qt::DecorationRole:
{
// FIXME: replace with an icon cache
return cachedIcon;
IconCache * ic = IconCache::instance();
// FIXME: replace with an icon cache/renderer
/*
QString path = ":/icons/instances/";
path += pdata->iconKey();
QIcon icon(path);
*/
QString key = pdata->iconKey();
return ic->getIcon(key);
//else return QIcon(":/icons/multimc/scalable/apps/multimc.svg");
}
// for now.
case KCategorizedSortFilterProxyModel::CategorySortRole:
case KCategorizedSortFilterProxyModel::CategoryDisplayRole:
{
return "IT'S A GROUP";
return pdata->group();
}
default:
break;

View File

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

View File

@ -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 File

@ -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;
};

View File

@ -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>&amp;Force Update</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rememberUsernameCheckbox">
<property name="sizePolicy">

View File

@ -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 File

@ -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

View File

@ -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 File

@ -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 File

@ -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;
*/
}
}
}

View File

@ -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 File

@ -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.

View File

@ -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 File

@ -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

View File

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

View File

@ -17,16 +17,14 @@
#define INSTANCELIST_H
#include <QObject>
#include <QSharedPointer>
#include "siglist.h"
#include "instance.h"
#include "libmmc_config.h"
class Instance;
class LIBMULTIMC_EXPORT InstanceList : public QObject, public SigList< QSharedPointer<Instance> >
class LIBMULTIMC_EXPORT InstanceList : public QObject
{
Q_OBJECT
public:
@ -46,14 +44,46 @@ public:
QString instDir() const { return m_instDir; }
/*!
* \brief Loads the instance list.
* \brief Loads the instance list. Triggers notifications.
*/
InstListError loadList();
DEFINE_SIGLIST_SIGNALS(QSharedPointer<Instance>);
SETUP_SIGLIST_SIGNALS(QSharedPointer<Instance>);
/*!
* \brief Get the instance at index
*/
InstancePtr at(int i) const
{
return m_instances.at(i);
};
/*!
* \brief Get the count of loaded instances
*/
int count() const
{
return m_instances.count();
};
/// Clear all instances. Triggers notifications.
void clear();
/// Add an instance. Triggers notifications, returns the new index
int add(InstancePtr t);
/// Get an instance by ID
InstancePtr getInstanceById (QString id);
signals:
void instanceAdded(int index);
void instanceChanged(int index);
void invalidated();
private slots:
void propertiesChanged(Instance * inst);
protected:
QString m_instDir;
QList< InstancePtr > m_instances;
};
#endif // INSTANCELIST_H

View File

@ -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();
};

View File

@ -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 File

@ -15,17 +15,21 @@
#include "include/instancelist.h"
#include "siglist_impl.h"
#include <QDir>
#include <QFile>
#include <QDirIterator>
#include <QThread>
#include <QTextStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include "include/instance.h"
#include "include/instanceloader.h"
#include "pathutils.h"
const static int GROUP_FILE_FORMAT_VERSION = 1;
InstanceList::InstanceList(const QString &instDir, QObject *parent) :
QObject(parent), m_instDir("instances")
@ -38,6 +42,104 @@ InstanceList::InstListError InstanceList::loadList()
QDir dir(m_instDir);
QDirIterator iter(dir);
QString groupFileName = m_instDir + "/instgroups.json";
// temporary map from instance ID to group name
QMap<QString, QString> groupMap;
// HACK: this is really an if. breaks after one iteration.
while (QFileInfo(groupFileName).exists())
{
QFile groupFile(groupFileName);
if (!groupFile.open(QIODevice::ReadOnly))
{
// An error occurred. Ignore it.
qDebug("Failed to read instance group file.");
break;
}
QTextStream in(&groupFile);
QString jsonStr = in.readAll();
groupFile.close();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
if (error.error != QJsonParseError::NoError)
{
qWarning(QString("Failed to parse instance group file: %1 at offset %2").
arg(error.errorString(), QString::number(error.offset)).toUtf8());
break;
}
if (!jsonDoc.isObject())
{
qWarning("Invalid group file. Root entry should be an object.");
break;
}
QJsonObject rootObj = jsonDoc.object();
// Make sure the format version matches.
if (rootObj.value("formatVersion").toVariant().toInt() == GROUP_FILE_FORMAT_VERSION)
{
// Get the group list.
if (!rootObj.value("groups").isObject())
{
qWarning("Invalid group list JSON: 'groups' should be an object.");
break;
}
// Iterate through the list.
QJsonObject groupList = rootObj.value("groups").toObject();
for (QJsonObject::iterator iter = groupList.begin();
iter != groupList.end(); iter++)
{
QString groupName = iter.key();
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning(QString("Group '%1' in the group list should "
"be an object.").arg(groupName).toUtf8());
continue;
}
QJsonObject groupObj = iter.value().toObject();
/*
// Create the group object.
InstanceGroup *group = new InstanceGroup(groupName, this);
groups.push_back(group);
// If 'hidden' isn't a bool value, just assume it's false.
if (groupObj.value("hidden").isBool() && groupObj.value("hidden").toBool())
{
group->setHidden(groupObj.value("hidden").toBool());
}
*/
if (!groupObj.value("instances").isArray())
{
qWarning(QString("Group '%1' in the group list is invalid. "
"It should contain an array "
"called 'instances'.").arg(groupName).toUtf8());
continue;
}
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin();
iter2 != instancesArray.end(); iter2++)
{
groupMap[(*iter2).toString()] = groupName;
}
}
}
break;
}
m_instances.clear();
while (iter.hasNext())
{
QString subDir = iter.next();
@ -75,13 +177,61 @@ InstanceList::InstListError InstanceList::loadList()
else
{
QSharedPointer<Instance> inst(instPtr);
auto iter = groupMap.find(inst->id());
if(iter != groupMap.end())
{
inst->setGroup((*iter));
}
qDebug(QString("Loaded instance %1").arg(inst->name()).toUtf8());
inst->setParent(this);
append(QSharedPointer<Instance>(inst));
m_instances.append(inst);
connect(instPtr, SIGNAL(propertiesChanged(Instance*)),this, SLOT(propertiesChanged(Instance*)));
}
}
}
emit invalidated();
return NoError;
}
/// Clear all instances. Triggers notifications.
void InstanceList::clear()
{
m_instances.clear();
emit invalidated();
};
/// Add an instance. Triggers notifications, returns the new index
int InstanceList::add(InstancePtr t)
{
m_instances.append(t);
emit instanceAdded(count() - 1);
return count() - 1;
}
InstancePtr InstanceList::getInstanceById(QString instId)
{
QListIterator<InstancePtr> iter(m_instances);
InstancePtr inst;
while(iter.hasNext())
{
inst = iter.next();
if (inst->id() == instId)
break;
}
if (inst->id() != instId)
return InstancePtr();
else
return iter.peekPrevious();
}
void InstanceList::propertiesChanged(Instance * inst)
{
for(int i = 0; i < m_instances.count(); i++)
{
if(inst == m_instances[i].data())
{
emit instanceChanged(i);
break;
}
}
}

View File

@ -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 File

@ -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 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

View File

@ -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());
}
}

View File

@ -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());
}
}

View 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;

View File

@ -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

View 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)
{
}

View 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

View File

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

View File

@ -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;
};

View File

@ -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);

View File

@ -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;

View File

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

BIN
resources/catbgrnd2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -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

View File

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

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

60
test.cpp Normal file
View 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;
}
}