From ccbf341dc8d8e515d9cf918bff7ff9435c477847 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Tue, 24 Dec 2013 11:47:30 +0100 Subject: [PATCH 01/49] Initial commit. Basics work. Next: Drag and Drop --- .gitignore | 2 + CMakeLists.txt | 35 +++ CategorizedProxyModel.cpp | 12 + CategorizedProxyModel.h | 18 ++ CategorizedView.cpp | 587 ++++++++++++++++++++++++++++++++++++++ CategorizedView.h | 96 +++++++ main.cpp | 53 ++++ 7 files changed, 803 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CategorizedProxyModel.cpp create mode 100644 CategorizedProxyModel.h create mode 100644 CategorizedView.cpp create mode 100644 CategorizedView.h create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a5d18fa3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +*.user* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..8a246bcf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 2.8.9) + +project(GroupView) + +set(CMAKE_AUTOMOC ON) + +IF(APPLE) + message(STATUS "Using APPLE CMAKE_CXX_FLAGS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") +ELSEIF(UNIX) + # assume GCC, add C++0x/C++11 stuff + MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") +ELSEIF(MINGW) + MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall") +ENDIF() + +find_package(Qt5Core REQUIRED) +find_package(Qt5Gui REQUIRED) +find_package(Qt5Widgets REQUIRED) + +include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS}) + +set(SOURCES + main.cpp + + CategorizedView.h + CategorizedView.cpp + CategorizedProxyModel.h + CategorizedProxyModel.cpp +) + +add_executable(GroupView ${SOURCES}) +qt5_use_modules(GroupView Core Gui Widgets) diff --git a/CategorizedProxyModel.cpp b/CategorizedProxyModel.cpp new file mode 100644 index 00000000..2b54ce1b --- /dev/null +++ b/CategorizedProxyModel.cpp @@ -0,0 +1,12 @@ +#include "CategorizedProxyModel.h" + +#include "CategorizedView.h" + +CategorizedProxyModel::CategorizedProxyModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} +bool CategorizedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + return left.data(CategorizedView::CategoryRole).toString() < right.data(CategorizedView::CategoryRole).toString(); +} diff --git a/CategorizedProxyModel.h b/CategorizedProxyModel.h new file mode 100644 index 00000000..6e4f3fdc --- /dev/null +++ b/CategorizedProxyModel.h @@ -0,0 +1,18 @@ +#ifndef CATEGORIZEDPROXYMODEL_H +#define CATEGORIZEDPROXYMODEL_H + +#include + +class CategorizedProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + CategorizedProxyModel(QObject *parent = 0); + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; +}; + + +#endif // CATEGORIZEDPROXYMODEL_H diff --git a/CategorizedView.cpp b/CategorizedView.cpp new file mode 100644 index 00000000..46b1e072 --- /dev/null +++ b/CategorizedView.cpp @@ -0,0 +1,587 @@ +#include "CategorizedView.h" + +#include +#include +#include +#include +#include + +CategorizedView::Category::Category(const QString &text, CategorizedView *view) + : view(view), text(text), collapsed(false) +{ +} +CategorizedView::Category::Category(const CategorizedView::Category *other) : + view(other->view), text(other->text), collapsed(other->collapsed), iconRect(other->iconRect), textRect(other->textRect) +{ +} + +void CategorizedView::Category::drawHeader(QPainter *painter, const int y) +{ + painter->save(); + + int height = headerHeight() - 4; + int collapseSize = height; + + // the icon + iconRect = QRect(view->m_rightMargin + 2, 2 + y, collapseSize, collapseSize); + painter->setPen(QPen(Qt::black, 1)); + painter->drawRect(iconRect); + static const int margin = 2; + QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); + int midX = iconSubrect.center().x(); + int midY = iconSubrect.center().y(); + if (collapsed) + { + painter->drawLine(midX, iconSubrect.top(), midX, iconSubrect.bottom()); + } + painter->drawLine(iconSubrect.left(), midY, iconSubrect.right(), midY); + + // the text + int textWidth = painter->fontMetrics().width(text); + textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); + painter->drawText(textRect, text, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); + + // the line + painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); + + painter->restore(); +} + +int CategorizedView::Category::totalHeight() const +{ + return headerHeight() + 5 + contentHeight(); +} +int CategorizedView::Category::headerHeight() const +{ + return qApp->fontMetrics().height() + 4; +} +int CategorizedView::Category::contentHeight() const +{ + if (collapsed) + { + return 0; + } + const int rows = qMax(1, qCeil((qreal)view->numItemsForCategory(this) / (qreal)view->itemsPerRow())); + return view->itemSize().height() * rows; +} +QSize CategorizedView::Category::categoryTotalSize() const +{ + return QSize(view->contentWidth(), contentHeight()); +} + +CategorizedView::CategorizedView(QWidget *parent) + : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_categoryMargin(5)//, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) +{ + setViewMode(IconMode); + setMovement(Snap); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setWordWrap(true); +} + +CategorizedView::~CategorizedView() +{ + qDeleteAll(m_categories); + m_categories.clear(); +} + +void CategorizedView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +{ +// if (m_updatesDisabled) +// { +// return; +// } + + QListView::dataChanged(topLeft, bottomRight, roles); + + if (roles.contains(CategoryRole)) + { + updateGeometries(); + update(); + } +} +void CategorizedView::rowsInserted(const QModelIndex &parent, int start, int end) +{ +// if (m_updatesDisabled) +// { +// return; +// } + + QListView::rowsInserted(parent, start, end); + + updateGeometries(); + update(); +} +void CategorizedView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ +// if (m_updatesDisabled) +// { +// return; +// } + + QListView::rowsAboutToBeRemoved(parent, start, end); + + updateGeometries(); + update(); +} + +void CategorizedView::updateGeometries() +{ + QListView::updateGeometries(); + + m_cachedItemSize = QSize(); + + QMap cats; + + for (int i = 0; i < model()->rowCount(); ++i) + { + const QString category = model()->index(i, 0).data(CategoryRole).toString(); + if (!cats.contains(category)) + { + Category *old = this->category(category); + if (old) + { + cats.insert(category, new Category(old)); + } + else + { + cats.insert(category, new Category(category, this)); + } + } + } + + /*if (m_editedCategory) + { + m_editedCategory = cats[m_editedCategory->text]; + }*/ + + qDeleteAll(m_categories); + m_categories = cats.values(); + + update(); +} + +bool CategorizedView::isIndexHidden(const QModelIndex &index) const +{ + Category *cat = category(index); + if (cat) + { + return cat->collapsed; + } + else + { + return false; + } +} + +CategorizedView::Category *CategorizedView::category(const QModelIndex &index) const +{ + return category(index.data(CategoryRole).toString()); +} +CategorizedView::Category *CategorizedView::category(const QString &cat) const +{ + for (int i = 0; i < m_categories.size(); ++i) + { + if (m_categories.at(i)->text == cat) + { + return m_categories.at(i); + } + } + return 0; +} + +int CategorizedView::numItemsForCategory(const CategorizedView::Category *category) const +{ + return itemsForCategory(category).size(); +} +QList CategorizedView::itemsForCategory(const CategorizedView::Category *category) const +{ + QList indices; + for (int i = 0; i < model()->rowCount(); ++i) + { + if (model()->index(i, 0).data(CategoryRole).toString() == category->text) + { + indices += model()->index(i, 0); + } + } + return indices; +} + +int CategorizedView::categoryTop(const CategorizedView::Category *category) const +{ + int res = 0; + const QList cats = sortedCategories(); + for (int i = 0; i < cats.size(); ++i) + { + if (cats.at(i) == category) + { + break; + } + res += cats.at(i)->totalHeight() + m_categoryMargin; + } + return res; +} + +int CategorizedView::itemsPerRow() const +{ + return qFloor((qreal)contentWidth() / (qreal)itemSize().width()); +} +int CategorizedView::contentWidth() const +{ + return width() - m_leftMargin - m_rightMargin; +} + +bool CategorizedView::lessThanCategoryPointer(const CategorizedView::Category *c1, const CategorizedView::Category *c2) +{ + return c1->text < c2->text; +} +QList CategorizedView::sortedCategories() const +{ + QList out = m_categories; + qSort(out.begin(), out.end(), &CategorizedView::lessThanCategoryPointer); + return out; +} + +QSize CategorizedView::itemSize(const QStyleOptionViewItem &option) const +{ + if (!m_cachedItemSize.isValid()) + { + QModelIndex sample = model()->index(model()->rowCount() -1, 0); + const QAbstractItemDelegate *delegate = itemDelegate(); + if (delegate) + { + m_cachedItemSize = delegate->sizeHint(option, sample); + m_cachedItemSize.setWidth(m_cachedItemSize.width() + 20); + m_cachedItemSize.setHeight(m_cachedItemSize.height() + 20); + } + else + { + m_cachedItemSize = QSize(); + } + } + return m_cachedItemSize; +} + +void CategorizedView::mousePressEvent(QMouseEvent *event) +{ + //endCategoryEditor(); + + if (event->buttons() & Qt::LeftButton) + { + foreach (Category *category, m_categories) + { + if (category->iconRect.contains(event->pos())) + { + category->collapsed = !category->collapsed; + updateGeometries(); + viewport()->update(); + event->accept(); + return; + } + } + + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + if (visualRect(index).contains(event->pos())) + { + selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); + event->accept(); + return; + } + } + } + + QListView::mousePressEvent(event); +} +void CategorizedView::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) + { + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + if (visualRect(index).contains(event->pos())) + { + selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); + event->accept(); + return; + } + } + } + + QListView::mouseMoveEvent(event); +} +void CategorizedView::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) + { + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + if (visualRect(index).contains(event->pos())) + { + selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); + event->accept(); + return; + } + } + } + + QListView::mouseReleaseEvent(event); +} +void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) +{ + /*endCategoryEditor(); + + foreach (Category *category, m_categories) + { + if (category->textRect.contains(event->pos()) && m_categoryEditor == 0) + { + startCategoryEditor(category); + event->accept(); + return; + } + }*/ + + QListView::mouseDoubleClickEvent(event); +} +void CategorizedView::paintEvent(QPaintEvent *event) +{ + QPainter painter(this->viewport()); + + int y = 0; + for (int i = 0; i < m_categories.size(); ++i) + { + Category *category = m_categories.at(i); + category->drawHeader(&painter, y); + y += category->totalHeight() + m_categoryMargin; + } + + for (int i = 0; i < model()->rowCount(); ++i) + { + const QModelIndex index = model()->index(i, 0); + if (isIndexHidden(index)) + { + continue; + } + Qt::ItemFlags flags = index.flags(); + QStyleOptionViewItemV4 option(viewOptions()); + option.rect = visualRect(index); + option.widget = this; + option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText : QStyleOptionViewItemV2::None; + if (flags & Qt::ItemIsSelectable) + { + option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected : QStyle::State_None; + } + else + { + option.state &= ~QStyle::State_Selected; + } + option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None; + if (!(flags & Qt::ItemIsEnabled)) + { + option.state &= ~QStyle::State_Enabled; + } + itemDelegate()->paint(&painter, option, index); + } +} +void CategorizedView::resizeEvent(QResizeEvent *event) +{ + QListView::resizeEvent(event); + +// if (m_categoryEditor) +// { +// m_categoryEditor->resize(qMax(contentWidth() / 2, m_editedCategory->textRect.width()), m_categoryEditor->height()); +// } + + updateGeometries(); +} + +void CategorizedView::dragEnterEvent(QDragEnterEvent *event) +{ + // TODO +} +void CategorizedView::dragMoveEvent(QDragMoveEvent *event) +{ + // TODO +} +void CategorizedView::dragLeaveEvent(QDragLeaveEvent *event) +{ + // TODO +} +void CategorizedView::dropEvent(QDropEvent *event) +{ + stopAutoScroll(); + setState(NoState); + + if (event->source() != this || !(event->possibleActions() & Qt::MoveAction)) + { + return; + } + + // check that we aren't on a category header and calculate which category we're in + Category *category = 0; + { + int y = 0; + foreach (Category *cat, m_categories) + { + if (event->pos().y() > y && event->pos().y() < (y + cat->headerHeight())) + { + viewport()->update(); + return; + } + y += cat->totalHeight() + m_categoryMargin; + if (event->pos().y() < y) + { + category = cat; + break; + } + } + } + + // calculate the internal column + int internalColumn = -1; + { + const int itemWidth = itemSize().width(); + for (int i = 0, c = 0; + i < contentWidth(); + i += itemWidth, ++c) + { + if (event->pos().x() > (i - itemWidth / 2) && + event->pos().x() < (i + itemWidth / 2)) + { + internalColumn = c; + break; + } + } + if (internalColumn == -1) + { + viewport()->update(); + return; + } + } + + // calculate the internal row + int internalRow = -1; + { + const int itemHeight = itemSize().height(); + const int top = categoryTop(category); + for (int i = top + category->headerHeight(), r = 0; + i < top + category->totalHeight(); + i += itemHeight, ++r) + { + if (event->pos().y() > i && event->pos().y() < (i + itemHeight)) + { + internalRow = r; + break; + } + } + if (internalRow == -1) + { + viewport()->update(); + return; + } + } + + QList indices = itemsForCategory(category); + + // flaten the internalColumn/internalRow to one row + int categoryRow; + { + for (int i = 0; i < internalRow; ++i) + { + if (i == internalRow) + { + break; + } + categoryRow += itemsPerRow(); + } + categoryRow += internalColumn; + } + + int row = indices.at(categoryRow).row(); + if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + } + updateGeometries(); +} + +bool lessThanQModelIndex(const QModelIndex &i1, const QModelIndex &i2) +{ + return i1.data() < i2.data(); +} +QRect CategorizedView::visualRect(const QModelIndex &index) const +{ + if (!index.isValid() || isIndexHidden(index) || index.column() > 0) + { + return QRect(); + } + + const Category *cat = category(index); + QList indices = itemsForCategory(cat); + qSort(indices.begin(), indices.end(), &lessThanQModelIndex); + int x = 0; + int y = 0; + const int perRow = itemsPerRow(); + for (int i = 0; i < indices.size(); ++i) + { + if (indices.at(i) == index) + { + break; + } + ++x; + if (x == perRow) + { + x = 0; + ++y; + } + } + + QSize size = itemSize(); + + QRect out; + out.setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height()); + out.setLeft(x * size.width()); + out.setSize(size); + + return out; +} +/* +void CategorizedView::startCategoryEditor(Category *category) +{ + if (m_categoryEditor != 0) + { + return; + } + m_editedCategory = category; + m_categoryEditor = new QLineEdit(m_editedCategory->text, this); + QRect rect = m_editedCategory->textRect; + rect.setWidth(qMax(contentWidth() / 2, rect.width())); + m_categoryEditor->setGeometry(rect); + m_categoryEditor->show(); + m_categoryEditor->setFocus(); + connect(m_categoryEditor, &QLineEdit::returnPressed, this, &CategorizedView::endCategoryEditor); +} + +void CategorizedView::endCategoryEditor() +{ + if (m_categoryEditor == 0) + { + return; + } + m_editedCategory->text = m_categoryEditor->text(); + m_updatesDisabled = true; + foreach (const QModelIndex &index, itemsForCategory(m_editedCategory)) + { + const_cast(index.model())->setData(index, m_categoryEditor->text(), CategoryRole); + } + m_updatesDisabled = false; + delete m_categoryEditor; + m_categoryEditor = 0; + m_editedCategory = 0; + updateGeometries(); +} +*/ diff --git a/CategorizedView.h b/CategorizedView.h new file mode 100644 index 00000000..1e918496 --- /dev/null +++ b/CategorizedView.h @@ -0,0 +1,96 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include +#include + +class CategorizedView : public QListView +{ + Q_OBJECT + +public: + CategorizedView(QWidget *parent = 0); + ~CategorizedView(); + + enum + { + CategoryRole = Qt::UserRole + }; + + virtual QRect visualRect(const QModelIndex &index) const; + +protected slots: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); + virtual void rowsInserted(const QModelIndex &parent, int start, int end); + virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + virtual void updateGeometries(); + +protected: + virtual bool isIndexHidden(const QModelIndex &index) const; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent *event) override; + +private: + struct Category + { + Category(const QString &text, CategorizedView *view); + Category(const Category *other); + CategorizedView *view; + QString text; + bool collapsed; + QRect iconRect; + QRect textRect; + + void drawHeader(QPainter *painter, const int y); + int totalHeight() const; + int headerHeight() const; + int contentHeight() const; + QSize categoryTotalSize() const; + }; + friend struct Category; + + QList m_categories; + + int m_leftMargin; + int m_rightMargin; + int m_categoryMargin; + int m_itemSpacing; + + //bool m_updatesDisabled; + + Category *category(const QModelIndex &index) const; + Category *category(const QString &cat) const; + int numItemsForCategory(const Category *category) const; + QList itemsForCategory(const Category *category) const; + + int categoryTop(const Category *category) const; + + int itemsPerRow() const; + int contentWidth() const; + + static bool lessThanCategoryPointer(const Category *c1, const Category *c2); + QList sortedCategories() const; + +private: + mutable QSize m_cachedItemSize; + QSize itemSize(const QStyleOptionViewItem &option) const; + QSize itemSize() const { return itemSize(viewOptions()); } + + /*QLineEdit *m_categoryEditor; + Category *m_editedCategory; + void startCategoryEditor(Category *category); + +private slots: + void endCategoryEditor();*/ +}; + +#endif // WIDGET_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 00000000..24d7075e --- /dev/null +++ b/main.cpp @@ -0,0 +1,53 @@ +#include "CategorizedView.h" +#include +#include + +#include "CategorizedProxyModel.h" + +QPixmap icon(const Qt::GlobalColor color) +{ + QPixmap p = QPixmap(32, 32); + p.fill(QColor(color)); + return p; +} +QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, const QString &category) +{ + QStandardItem *item = new QStandardItem; + item->setText(text); + item->setData(icon(color), Qt::DecorationRole); + item->setData(category, CategorizedView::CategoryRole); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + return item; +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QStandardItemModel model; + model.setRowCount(10); + model.setColumnCount(1); + + model.setItem(0, createItem(Qt::red, "Red", "Colorful")); + model.setItem(1, createItem(Qt::blue, "Blue", "Colorful")); + model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful")); + + model.setItem(3, createItem(Qt::black, "Black", "Not Colorful")); + model.setItem(4, createItem(Qt::darkGray, "Dark Gray", "Not Colorful")); + model.setItem(5, createItem(Qt::gray, "Gray", "Not Colorful")); + model.setItem(6, createItem(Qt::lightGray, "Light Gray", "Not Colorful")); + model.setItem(7, createItem(Qt::white, "White", "Not Colorful")); + + model.setItem(8, createItem(Qt::darkGreen, "Dark Green", "")); + model.setItem(9, createItem(Qt::green, "Green", "")); + + CategorizedProxyModel pModel; + pModel.setSourceModel(&model); + + CategorizedView w; + w.setModel(&pModel); + w.resize(640, 480); + w.show(); + + return a.exec(); +} From 525f508d94120feae89ee1d1bd960625ab14ed37 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 26 Dec 2013 21:16:03 +0100 Subject: [PATCH 02/49] Loads of stuff, amongst others d&d and many bug fixes --- CategorizedProxyModel.cpp | 11 +- CategorizedView.cpp | 707 ++++++++++++++++++++++++++++---------- CategorizedView.h | 25 ++ 3 files changed, 568 insertions(+), 175 deletions(-) diff --git a/CategorizedProxyModel.cpp b/CategorizedProxyModel.cpp index 2b54ce1b..e4a7563a 100644 --- a/CategorizedProxyModel.cpp +++ b/CategorizedProxyModel.cpp @@ -8,5 +8,14 @@ CategorizedProxyModel::CategorizedProxyModel(QObject *parent) } bool CategorizedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - return left.data(CategorizedView::CategoryRole).toString() < right.data(CategorizedView::CategoryRole).toString(); + const QString leftCategory = left.data(CategorizedView::CategoryRole).toString(); + const QString rightCategory = right.data(CategorizedView::CategoryRole).toString(); + if (leftCategory == rightCategory) + { + return left.row() < right.row(); + } + else + { + return leftCategory < rightCategory; + } } diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 46b1e072..95505fd7 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -5,6 +5,23 @@ #include #include #include +#include +#include +#include +#include + +template +bool listsIntersect(const QList &l1, const QList t2) +{ + foreach (const T &item, l1) + { + if (t2.contains(item)) + { + return true; + } + } + return false; +} CategorizedView::Category::Category(const QString &text, CategorizedView *view) : view(view), text(text), collapsed(false) @@ -39,7 +56,7 @@ void CategorizedView::Category::drawHeader(QPainter *painter, const int y) // the text int textWidth = painter->fontMetrics().width(text); textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); - painter->drawText(textRect, text, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); + view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, view->palette(), true, text); // the line painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); @@ -77,6 +94,11 @@ CategorizedView::CategorizedView(QWidget *parent) setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setWordWrap(true); + setDragDropMode(QListView::InternalMove); + setAcceptDrops(true); + + m_cachedCategoryToIndexMapping.setMaxCost(50); + m_cachedVisualRects.setMaxCost(50); } CategorizedView::~CategorizedView() @@ -130,6 +152,8 @@ void CategorizedView::updateGeometries() QListView::updateGeometries(); m_cachedItemSize = QSize(); + m_cachedCategoryToIndexMapping.clear(); + m_cachedVisualRects.clear(); QMap cats; @@ -189,6 +213,17 @@ CategorizedView::Category *CategorizedView::category(const QString &cat) const } return 0; } +CategorizedView::Category *CategorizedView::categoryAt(const QPoint &pos) const +{ + for (int i = 0; i < m_categories.size(); ++i) + { + if (m_categories.at(i)->iconRect.contains(pos)) + { + return m_categories.at(i); + } + } + return 0; +} int CategorizedView::numItemsForCategory(const CategorizedView::Category *category) const { @@ -196,15 +231,47 @@ int CategorizedView::numItemsForCategory(const CategorizedView::Category *catego } QList CategorizedView::itemsForCategory(const CategorizedView::Category *category) const { - QList indices; - for (int i = 0; i < model()->rowCount(); ++i) + if (!m_cachedCategoryToIndexMapping.contains(category)) { - if (model()->index(i, 0).data(CategoryRole).toString() == category->text) + QList *indices = new QList(); + for (int i = 0; i < model()->rowCount(); ++i) { - indices += model()->index(i, 0); + if (model()->index(i, 0).data(CategoryRole).toString() == category->text) + { + indices->append(model()->index(i, 0)); + } + } + m_cachedCategoryToIndexMapping.insert(category, indices, indices->size()); + } + return *m_cachedCategoryToIndexMapping.object(category); +} +QModelIndex CategorizedView::firstItemForCategory(const CategorizedView::Category *category) const +{ + QList indices = itemsForCategory(category); + QModelIndex first; + foreach (const QModelIndex &index, indices) + { + if (index.row() < first.row() || !first.isValid()) + { + first = index; } } - return indices; + + return first; +} +QModelIndex CategorizedView::lastItemForCategory(const CategorizedView::Category *category) const +{ + QList indices = itemsForCategory(category); + QModelIndex last; + foreach (const QModelIndex &index, indices) + { + if (index.row() > last.row() || !last.isValid()) + { + last = index; + } + } + + return last; } int CategorizedView::categoryTop(const CategorizedView::Category *category) const @@ -231,14 +298,10 @@ int CategorizedView::contentWidth() const return width() - m_leftMargin - m_rightMargin; } -bool CategorizedView::lessThanCategoryPointer(const CategorizedView::Category *c1, const CategorizedView::Category *c2) -{ - return c1->text < c2->text; -} QList CategorizedView::sortedCategories() const { QList out = m_categories; - qSort(out.begin(), out.end(), &CategorizedView::lessThanCategoryPointer); + qSort(out.begin(), out.end(), [](const Category *c1, const Category *c2) { return c1->text < c2->text; }); return out; } @@ -266,85 +329,186 @@ void CategorizedView::mousePressEvent(QMouseEvent *event) { //endCategoryEditor(); - if (event->buttons() & Qt::LeftButton) - { - foreach (Category *category, m_categories) - { - if (category->iconRect.contains(event->pos())) - { - category->collapsed = !category->collapsed; - updateGeometries(); - viewport()->update(); - event->accept(); - return; - } - } + QPoint pos = event->pos(); + QPersistentModelIndex index = indexAt(pos); - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - if (visualRect(index).contains(event->pos())) - { - selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); - event->accept(); - return; - } - } + m_pressedIndex = index; + m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); + QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); + QPoint offset = QPoint(horizontalOffset(), verticalOffset()); + if (!(command & QItemSelectionModel::Current)) + { + m_pressedPosition = pos + offset; + } + else if (!indexAt(m_pressedPosition - offset).isValid()) + { + m_pressedPosition = visualRect(currentIndex()).center() + offset; } - QListView::mousePressEvent(event); + m_pressedCategory = categoryAt(m_pressedPosition); + if (m_pressedCategory) + { + setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); + event->accept(); + return; + } + + if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) + { + // we disable scrollTo for mouse press so the item doesn't change position + // when the user is interacting with it (ie. clicking on it) + bool autoScroll = hasAutoScroll(); + setAutoScroll(false); + selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + setAutoScroll(autoScroll); + QRect rect(m_pressedPosition - offset, pos); + if (command.testFlag(QItemSelectionModel::Toggle)) + { + command &= ~QItemSelectionModel::Toggle; + m_ctrlDragSelectionFlag = selectionModel()->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; + command |= m_ctrlDragSelectionFlag; + } + setSelection(rect, command); + + // signal handlers may change the model + emit pressed(index); + + } else { + // Forces a finalize() even if mouse is pressed, but not on a item + selectionModel()->select(QModelIndex(), QItemSelectionModel::Select); + } } void CategorizedView::mouseMoveEvent(QMouseEvent *event) { - if (event->buttons() & Qt::LeftButton) + QPoint topLeft; + QPoint bottomRight = event->pos(); + + if (state() == ExpandingState || state() == CollapsingState) { - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - if (visualRect(index).contains(event->pos())) - { - selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); - event->accept(); - return; - } - } + return; } - QListView::mouseMoveEvent(event); + if (state() == DraggingState) + { + topLeft = m_pressedPosition - QPoint(horizontalOffset(), verticalOffset()); + if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) + { + m_pressedIndex = QModelIndex(); + startDrag(model()->supportedDragActions()); + setState(NoState); + stopAutoScroll(); + } + return; + } + + QPersistentModelIndex index = indexAt(bottomRight); + + if (selectionMode() != SingleSelection) + { + topLeft = m_pressedPosition - QPoint(horizontalOffset(), verticalOffset()); + } + else + { + topLeft = bottomRight; + } + + if (m_pressedIndex.isValid() + && (state() != DragSelectingState) + && (event->buttons() != Qt::NoButton) + && !selectedIndexes().isEmpty()) + { + setState(DraggingState); + return; + } + + if ((event->buttons() & Qt::LeftButton) && selectionModel()) + { + setState(DragSelectingState); + QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); + if (m_ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle)) + { + command &= ~QItemSelectionModel::Toggle; + command |= m_ctrlDragSelectionFlag; + } + + // Do the normalize ourselves, since QRect::normalized() is flawed + QRect selectionRect = QRect(topLeft, bottomRight); + setSelection(selectionRect, command); + + // set at the end because it might scroll the view + if (index.isValid() + && (index != selectionModel()->currentIndex()) + && (index.flags() & Qt::ItemIsEnabled)) + { + selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + } + } } void CategorizedView::mouseReleaseEvent(QMouseEvent *event) { - if (event->buttons() & Qt::LeftButton) - { - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - if (visualRect(index).contains(event->pos())) - { - selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); - event->accept(); - return; - } - } - } + QPoint pos = event->pos(); + QPersistentModelIndex index = indexAt(pos); - QListView::mouseReleaseEvent(event); -} -void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) -{ - /*endCategoryEditor(); + bool click = (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(pos)); - foreach (Category *category, m_categories) + if (click && m_pressedCategory) { - if (category->textRect.contains(event->pos()) && m_categoryEditor == 0) + if (state() == ExpandingState) { - startCategoryEditor(category); + m_pressedCategory->collapsed = false; + updateGeometries(); + viewport()->update(); event->accept(); return; } - }*/ + else if (state() == CollapsingState) + { + m_pressedCategory->collapsed = true; + updateGeometries(); + viewport()->update(); + event->accept(); + return; + } + } - QListView::mouseDoubleClickEvent(event); + m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; + + setState(NoState); + + if (click) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(index); + } + QStyleOptionViewItem option = viewOptions(); + if (m_pressedAlreadySelected) + { + option.state |= QStyle::State_Selected; + } + if ((model()->flags(index) & Qt::ItemIsEnabled) + && style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) + { + emit activated(index); + } + } +} +void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (!index.isValid() + || !(index.flags() & Qt::ItemIsEnabled) + || (m_pressedIndex != index)) + { + QMouseEvent me(QEvent::MouseButtonPress, + event->localPos(), event->windowPos(), event->screenPos(), + event->button(), event->buttons(), event->modifiers()); + mousePressEvent(&me); + return; + } + // signal handlers may change the model + QPersistentModelIndex persistent = index; + emit doubleClicked(persistent); } void CategorizedView::paintEvent(QPaintEvent *event) { @@ -385,6 +549,33 @@ void CategorizedView::paintEvent(QPaintEvent *event) } itemDelegate()->paint(&painter, option, index); } + + if (!m_lastDragPosition.isNull()) + { + QPair pair = rowDropPos(m_lastDragPosition); + Category *category = pair.first; + int row = pair.second; + if (category) + { + int internalRow = row - firstItemForCategory(category).row(); + qDebug() << internalRow << numItemsForCategory(category) << model()->index(row, 0).data().toString(); + QLine line; + if (internalRow >= numItemsForCategory(category)) + { + QRect toTheRightOfRect = visualRect(lastItemForCategory(category)); + line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight()); + } + else + { + QRect toTheLeftOfRect = visualRect(model()->index(row, 0)); + line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft()); + } + painter.save(); + painter.setPen(QPen(Qt::black, 3)); + painter.drawLine(line); + painter.restore(); + } + } } void CategorizedView::resizeEvent(QResizeEvent *event) { @@ -400,18 +591,33 @@ void CategorizedView::resizeEvent(QResizeEvent *event) void CategorizedView::dragEnterEvent(QDragEnterEvent *event) { - // TODO + if (!isDragEventAccepted(event)) + { + return; + } + m_lastDragPosition = event->pos(); + viewport()->update(); + event->accept(); } void CategorizedView::dragMoveEvent(QDragMoveEvent *event) { - // TODO + if (!isDragEventAccepted(event)) + { + return; + } + m_lastDragPosition = event->pos(); + viewport()->update(); + event->accept(); } void CategorizedView::dragLeaveEvent(QDragLeaveEvent *event) { - // TODO + m_lastDragPosition = QPoint(); + viewport()->update(); } void CategorizedView::dropEvent(QDropEvent *event) { + m_lastDragPosition = QPoint(); + stopAutoScroll(); setState(NoState); @@ -420,93 +626,68 @@ void CategorizedView::dropEvent(QDropEvent *event) return; } - // check that we aren't on a category header and calculate which category we're in - Category *category = 0; + QPair dropPos = rowDropPos(event->pos()); + const Category *category = dropPos.first; + const int row = dropPos.second; + + if (row == -1) { - int y = 0; - foreach (Category *cat, m_categories) - { - if (event->pos().y() > y && event->pos().y() < (y + cat->headerHeight())) - { - viewport()->update(); - return; - } - y += cat->totalHeight() + m_categoryMargin; - if (event->pos().y() < y) - { - category = cat; - break; - } - } + viewport()->update(); + return; } - // calculate the internal column - int internalColumn = -1; - { - const int itemWidth = itemSize().width(); - for (int i = 0, c = 0; - i < contentWidth(); - i += itemWidth, ++c) - { - if (event->pos().x() > (i - itemWidth / 2) && - event->pos().x() < (i + itemWidth / 2)) - { - internalColumn = c; - break; - } - } - if (internalColumn == -1) - { - viewport()->update(); - return; - } - } - - // calculate the internal row - int internalRow = -1; - { - const int itemHeight = itemSize().height(); - const int top = categoryTop(category); - for (int i = top + category->headerHeight(), r = 0; - i < top + category->totalHeight(); - i += itemHeight, ++r) - { - if (event->pos().y() > i && event->pos().y() < (i + itemHeight)) - { - internalRow = r; - break; - } - } - if (internalRow == -1) - { - viewport()->update(); - return; - } - } - - QList indices = itemsForCategory(category); - - // flaten the internalColumn/internalRow to one row - int categoryRow; - { - for (int i = 0; i < internalRow; ++i) - { - if (i == internalRow) - { - break; - } - categoryRow += itemsPerRow(); - } - categoryRow += internalColumn; - } - - int row = indices.at(categoryRow).row(); + const QString categoryText = category->text; if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) { + model()->setData(model()->index(row, 0), categoryText, CategoryRole); event->setDropAction(Qt::MoveAction); event->accept(); } updateGeometries(); + viewport()->update(); +} + +void CategorizedView::startDrag(Qt::DropActions supportedActions) +{ + QModelIndexList indexes = selectionModel()->selectedIndexes(); + if (indexes.count() > 0) + { + QMimeData *data = model()->mimeData(indexes); + if (!data) + { + return; + } + QRect rect; + QPixmap pixmap = renderToPixmap(indexes, &rect); + rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); + QDrag *drag = new QDrag(this); + drag->setPixmap(pixmap); + drag->setMimeData(data); + drag->setHotSpot(m_pressedPosition - rect.topLeft()); + Qt::DropAction defaultDropAction = Qt::IgnoreAction; + if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) + { + defaultDropAction = this->defaultDropAction(); + } + if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) + { + const QItemSelection selection = selectionModel()->selection(); + + for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) { + QModelIndex parent = (*it).parent(); + if ((*it).left() != 0) + { + continue; + } + if ((*it).right() != (model()->columnCount(parent) - 1)) + { + continue; + } + int count = (*it).bottom() - (*it).top() + 1; + model()->removeRows((*it).top(), count, parent); + } + } + } } bool lessThanQModelIndex(const QModelIndex &i1, const QModelIndex &i2) @@ -520,34 +701,39 @@ QRect CategorizedView::visualRect(const QModelIndex &index) const return QRect(); } - const Category *cat = category(index); - QList indices = itemsForCategory(cat); - qSort(indices.begin(), indices.end(), &lessThanQModelIndex); - int x = 0; - int y = 0; - const int perRow = itemsPerRow(); - for (int i = 0; i < indices.size(); ++i) + if (!m_cachedVisualRects.contains(index)) { - if (indices.at(i) == index) + const Category *cat = category(index); + QList indices = itemsForCategory(cat); + qSort(indices.begin(), indices.end(), &lessThanQModelIndex); + int x = 0; + int y = 0; + const int perRow = itemsPerRow(); + for (int i = 0; i < indices.size(); ++i) { - break; - } - ++x; - if (x == perRow) - { - x = 0; - ++y; + if (indices.at(i) == index) + { + break; + } + ++x; + if (x == perRow) + { + x = 0; + ++y; + } } + + QSize size = itemSize(); + + QRect *out = new QRect; + out->setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height()); + out->setLeft(x * size.width()); + out->setSize(size); + + m_cachedVisualRects.insert(index, out); } - QSize size = itemSize(); - - QRect out; - out.setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height()); - out.setLeft(x * size.width()); - out.setSize(size); - - return out; + return *m_cachedVisualRects.object(index); } /* void CategorizedView::startCategoryEditor(Category *category) @@ -585,3 +771,176 @@ void CategorizedView::endCategoryEditor() updateGeometries(); } */ + +QModelIndex CategorizedView::indexAt(const QPoint &point) const +{ + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + if (visualRect(index).contains(point)) + { + return index; + } + } + return QModelIndex(); +} +void CategorizedView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) +{ + QItemSelection selection; + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + if (visualRect(index).intersects(rect)) + { + selection.merge(QItemSelection(index, index), QItemSelectionModel::Select); + } + } + selectionModel()->select(selection, commands); +} + +QPixmap CategorizedView::renderToPixmap(const QModelIndexList &indices, QRect *r) const +{ + Q_ASSERT(r); + QList > paintPairs = draggablePaintPairs(indices, r); + if (paintPairs.isEmpty()) + { + return QPixmap(); + } + QPixmap pixmap(r->size()); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + QStyleOptionViewItem option = viewOptions(); + option.state |= QStyle::State_Selected; + for (int j = 0; j < paintPairs.count(); ++j) + { + option.rect = paintPairs.at(j).first.translated(-r->topLeft()); + const QModelIndex ¤t = paintPairs.at(j).second; + itemDelegate()->paint(&painter, option, current); + } + return pixmap; +} +QList > CategorizedView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const +{ + Q_ASSERT(r); + QRect &rect = *r; + const QRect viewportRect = viewport()->rect(); + QList > ret; + for (int i = 0; i < indices.count(); ++i) { + const QModelIndex &index = indices.at(i); + const QRect current = visualRect(index); + if (current.intersects(viewportRect)) { + ret += qMakePair(current, index); + rect |= current; + } + } + rect &= viewportRect; + return ret; +} + +bool CategorizedView::isDragEventAccepted(QDropEvent *event) +{ + if (event->source() != this) + { + return false; + } + if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes())) + { + return false; + } + if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), rowDropPos(event->pos()).second, 0, QModelIndex())) + { + return false; + } + return true; +} +QPair CategorizedView::rowDropPos(const QPoint &pos) +{ + // check that we aren't on a category header and calculate which category we're in + Category *category = 0; + { + int y = 0; + foreach (Category *cat, m_categories) + { + if (pos.y() > y && pos.y() < (y + cat->headerHeight())) + { + return qMakePair(nullptr, -1); + } + y += cat->totalHeight() + m_categoryMargin; + if (pos.y() < y) + { + category = cat; + break; + } + } + if (category == 0) + { + return qMakePair(nullptr, -1); + } + } + + // calculate the internal column + int internalColumn = -1; + { + const int itemWidth = itemSize().width(); + for (int i = 0, c = 0; + i < contentWidth(); + i += itemWidth, ++c) + { + if (pos.x() > (i - itemWidth / 2) && + pos.x() < (i + itemWidth / 2)) + { + internalColumn = c; + break; + } + } + if (internalColumn == -1) + { + return qMakePair(nullptr, -1); + } + } + + // calculate the internal row + int internalRow = -1; + { + const int itemHeight = itemSize().height(); + const int top = categoryTop(category); + for (int i = top + category->headerHeight(), r = 0; + i < top + category->totalHeight(); + i += itemHeight, ++r) + { + if (pos.y() > i && pos.y() < (i + itemHeight)) + { + internalRow = r; + break; + } + } + if (internalRow == -1) + { + return qMakePair(nullptr, -1); + } + } + + QList indices = itemsForCategory(category); + + // flaten the internalColumn/internalRow to one row + int categoryRow = 0; + { + for (int i = 0; i < internalRow; ++i) + { + if ((i + 1) >= internalRow) + { + break; + } + categoryRow += itemsPerRow(); + } + categoryRow += internalColumn; + } + + // this is used if we're past the last item + if (internalColumn >= qMin(itemsPerRow(), indices.size())) + { + return qMakePair(category, indices.last().row() + 1); + } + + return qMakePair(category, indices.at(categoryRow).row()); +} diff --git a/CategorizedView.h b/CategorizedView.h index 1e918496..0756629a 100644 --- a/CategorizedView.h +++ b/CategorizedView.h @@ -3,6 +3,7 @@ #include #include +#include class CategorizedView : public QListView { @@ -18,6 +19,8 @@ public: }; virtual QRect visualRect(const QModelIndex &index) const; + QModelIndex indexAt(const QPoint &point) const; + void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; protected slots: void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); @@ -39,6 +42,8 @@ protected: void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent *event) override; + void startDrag(Qt::DropActions supportedActions) override; + private: struct Category { @@ -59,6 +64,8 @@ private: friend struct Category; QList m_categories; + mutable QCache > m_cachedCategoryToIndexMapping; + mutable QCache m_cachedVisualRects; int m_leftMargin; int m_rightMargin; @@ -69,8 +76,11 @@ private: Category *category(const QModelIndex &index) const; Category *category(const QString &cat) const; + Category *categoryAt(const QPoint &pos) const; int numItemsForCategory(const Category *category) const; QList itemsForCategory(const Category *category) const; + QModelIndex firstItemForCategory(const Category *category) const; + QModelIndex lastItemForCategory(const Category *category) const; int categoryTop(const Category *category) const; @@ -91,6 +101,21 @@ private: private slots: void endCategoryEditor();*/ + +private: + QPoint m_pressedPosition; + QPersistentModelIndex m_pressedIndex; + bool m_pressedAlreadySelected; + Category *m_pressedCategory; + QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; + QPoint m_lastDragPosition; + + QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; + QList > draggablePaintPairs(const QModelIndexList &indices, QRect *r) const; + + bool isDragEventAccepted(QDropEvent *event); + + QPair rowDropPos(const QPoint &pos); }; #endif // WIDGET_H From c71808446b3e95e4fefb91b69c2cc51e4c4918cc Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 26 Dec 2013 21:23:21 +0100 Subject: [PATCH 03/49] Fix a bug --- CategorizedView.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 95505fd7..4d9a4a62 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -690,10 +690,6 @@ void CategorizedView::startDrag(Qt::DropActions supportedActions) } } -bool lessThanQModelIndex(const QModelIndex &i1, const QModelIndex &i2) -{ - return i1.data() < i2.data(); -} QRect CategorizedView::visualRect(const QModelIndex &index) const { if (!index.isValid() || isIndexHidden(index) || index.column() > 0) @@ -705,7 +701,6 @@ QRect CategorizedView::visualRect(const QModelIndex &index) const { const Category *cat = category(index); QList indices = itemsForCategory(cat); - qSort(indices.begin(), indices.end(), &lessThanQModelIndex); int x = 0; int y = 0; const int perRow = itemsPerRow(); From acbbdf319a7378a4029965a52222e7a84c33253f Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 26 Dec 2013 21:24:42 +0100 Subject: [PATCH 04/49] Remove a debug message --- CategorizedView.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 4d9a4a62..780674eb 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -558,7 +558,6 @@ void CategorizedView::paintEvent(QPaintEvent *event) if (category) { int internalRow = row - firstItemForCategory(category).row(); - qDebug() << internalRow << numItemsForCategory(category) << model()->index(row, 0).data().toString(); QLine line; if (internalRow >= numItemsForCategory(category)) { From 53db8edb851917809209e4473eef2a66651d6047 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 26 Dec 2013 22:02:25 +0100 Subject: [PATCH 05/49] Fixing several d&d bugs --- CategorizedView.cpp | 26 +++++++++++--------------- main.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 780674eb..1860f095 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -872,6 +872,8 @@ QPair CategorizedView::rowDropPos(const QPoint } } + QList indices = itemsForCategory(category); + // calculate the internal column int internalColumn = -1; { @@ -912,26 +914,20 @@ QPair CategorizedView::rowDropPos(const QPoint { return qMakePair(nullptr, -1); } + // this happens if we're in the margin between a one category and another + // categories header + if (internalRow > (indices.size() / itemsPerRow())) + { + return qMakePair(nullptr, -1); + } } - QList indices = itemsForCategory(category); - // flaten the internalColumn/internalRow to one row - int categoryRow = 0; - { - for (int i = 0; i < internalRow; ++i) - { - if ((i + 1) >= internalRow) - { - break; - } - categoryRow += itemsPerRow(); - } - categoryRow += internalColumn; - } + int categoryRow = internalRow * itemsPerRow() + internalColumn; // this is used if we're past the last item - if (internalColumn >= qMin(itemsPerRow(), indices.size())) + int numItemsInLastRow = indices.size() % itemsPerRow(); + if (internalColumn >= numItemsInLastRow) { return qMakePair(category, indices.last().row() + 1); } diff --git a/main.cpp b/main.cpp index 24d7075e..427ae17a 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ #include "CategorizedView.h" #include #include +#include #include "CategorizedProxyModel.h" @@ -10,6 +11,18 @@ QPixmap icon(const Qt::GlobalColor color) p.fill(QColor(color)); return p; } +QPixmap icon(const int number) +{ + QPixmap p = icon(Qt::white); + QPainter painter(&p); + QFont font = painter.font(); + font.setBold(true); + font.setPixelSize(28); + painter.setFont(font); + painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter, QString::number(number)); + painter.end(); + return p; +} QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, const QString &category) { QStandardItem *item = new QStandardItem; @@ -19,6 +32,15 @@ QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, cons item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); return item; } +QStandardItem *createItem(const int index, const QString &category) +{ + QStandardItem *item = new QStandardItem; + item->setText(QString("Item #%1").arg(index)); + item->setData(icon(index), Qt::DecorationRole); + item->setData(category, CategorizedView::CategoryRole); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + return item; +} int main(int argc, char *argv[]) { @@ -41,6 +63,11 @@ int main(int argc, char *argv[]) model.setItem(8, createItem(Qt::darkGreen, "Dark Green", "")); model.setItem(9, createItem(Qt::green, "Green", "")); + for (int i = 0; i < 21; ++i) + { + model.setItem(i + 10, createItem(i+1, "Items 1-20")); + } + CategorizedProxyModel pModel; pModel.setSourceModel(&model); From f8d835cd22de89bc130ff0413228cfea0ebfd8ac Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 26 Dec 2013 22:40:26 +0100 Subject: [PATCH 06/49] Fix scrolling --- CategorizedView.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 1860f095..6164cde6 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -9,6 +9,7 @@ #include #include #include +#include template bool listsIntersect(const QList &l1, const QList t2) @@ -182,6 +183,22 @@ void CategorizedView::updateGeometries() qDeleteAll(m_categories); m_categories = cats.values(); + if (m_categories.isEmpty()) + { + verticalScrollBar()->setRange(0, 0); + } + else + { + int totalHeight = 0; + foreach (const Category *category, m_categories) + { + totalHeight += category->totalHeight() + m_categoryMargin; + } + // remove the last margin (we don't want it) + totalHeight -= m_categoryMargin; + verticalScrollBar()->setRange(0, totalHeight- height()); + } + update(); } @@ -513,6 +530,8 @@ void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) void CategorizedView::paintEvent(QPaintEvent *event) { QPainter painter(this->viewport()); + QPoint offset(horizontalOffset(), verticalOffset()); + painter.translate(-offset); int y = 0; for (int i = 0; i < m_categories.size(); ++i) From 01092206783f74ce14f31d328cdac025fd90fe16 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 27 Dec 2013 00:03:24 +0100 Subject: [PATCH 07/49] Take the spacing into account --- CategorizedView.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 6164cde6..c134220f 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -97,6 +97,7 @@ CategorizedView::CategorizedView(QWidget *parent) setWordWrap(true); setDragDropMode(QListView::InternalMove); setAcceptDrops(true); + setSpacing(10); m_cachedCategoryToIndexMapping.setMaxCost(50); m_cachedVisualRects.setMaxCost(50); @@ -308,7 +309,7 @@ int CategorizedView::categoryTop(const CategorizedView::Category *category) cons int CategorizedView::itemsPerRow() const { - return qFloor((qreal)contentWidth() / (qreal)itemSize().width()); + return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + spacing())); } int CategorizedView::contentWidth() const { @@ -740,7 +741,7 @@ QRect CategorizedView::visualRect(const QModelIndex &index) const QRect *out = new QRect; out->setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height()); - out->setLeft(x * size.width()); + out->setLeft(spacing() + x * itemWidth() + x * spacing()); out->setSize(size); m_cachedVisualRects.insert(index, out); From 4662fbd29891ccb9120df82d17a34a7619242827 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 30 Dec 2013 18:45:40 +0100 Subject: [PATCH 08/49] Make the MultiMC delegate fully usable. Dynamic row heights. --- CMakeLists.txt | 2 + CategorizedView.cpp | 104 ++++++++++++------ CategorizedView.h | 9 +- InstanceDelegate.cpp | 254 +++++++++++++++++++++++++++++++++++++++++++ InstanceDelegate.h | 27 +++++ main.cpp | 4 +- 6 files changed, 363 insertions(+), 37 deletions(-) create mode 100644 InstanceDelegate.cpp create mode 100644 InstanceDelegate.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a246bcf..b94cf53e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,8 @@ set(SOURCES CategorizedView.cpp CategorizedProxyModel.h CategorizedProxyModel.cpp + InstanceDelegate.h + InstanceDelegate.cpp ) add_executable(GroupView ${SOURCES}) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index c134220f..bdb0b222 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -80,7 +80,22 @@ int CategorizedView::Category::contentHeight() const return 0; } const int rows = qMax(1, qCeil((qreal)view->numItemsForCategory(this) / (qreal)view->itemsPerRow())); - return view->itemSize().height() * rows; + QMap rowToHeightMapping; + foreach (const QModelIndex &index, view->itemsForCategory(this)) + { + int row = view->categoryInternalPosition(index).second; + if (!rowToHeightMapping.contains(row)) + { + rowToHeightMapping.insert(row, view->itemSize(index).height()); + } + } + int result = 0; + for (int i = 0; i < rows; ++i) + { + Q_ASSERT(rowToHeightMapping.contains(i)); + result += rowToHeightMapping[i]; + } + return result; } QSize CategorizedView::Category::categoryTotalSize() const { @@ -153,9 +168,10 @@ void CategorizedView::updateGeometries() { QListView::updateGeometries(); - m_cachedItemSize = QSize(); + m_cachedItemWidth = -1; m_cachedCategoryToIndexMapping.clear(); m_cachedVisualRects.clear(); + m_cachedItemSizes.clear(); QMap cats; @@ -323,24 +339,59 @@ QList CategorizedView::sortedCategories() const return out; } -QSize CategorizedView::itemSize(const QStyleOptionViewItem &option) const +int CategorizedView::itemWidth() const { - if (!m_cachedItemSize.isValid()) + if (m_cachedItemWidth == -1) { - QModelIndex sample = model()->index(model()->rowCount() -1, 0); - const QAbstractItemDelegate *delegate = itemDelegate(); - if (delegate) + m_cachedItemWidth = itemDelegate()->sizeHint(viewOptions(), model()->index(model()->rowCount() -1, 0)).width(); + } + return m_cachedItemWidth; +} + +QSize CategorizedView::itemSize(const QModelIndex &index) const +{ + if (!m_cachedItemSizes.contains(index)) + { + QModelIndexList indices; + int internalRow = categoryInternalPosition(index).second; + foreach (const QModelIndex &i, itemsForCategory(category(index))) { - m_cachedItemSize = delegate->sizeHint(option, sample); - m_cachedItemSize.setWidth(m_cachedItemSize.width() + 20); - m_cachedItemSize.setHeight(m_cachedItemSize.height() + 20); + if (categoryInternalPosition(i).second == internalRow) + { + indices.append(i); + } } - else + + int largestHeight = 0; + foreach (const QModelIndex &i, indices) { - m_cachedItemSize = QSize(); + largestHeight = qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); + } + m_cachedItemSizes.insert(index, new QSize(itemWidth(), largestHeight)); + } + return *m_cachedItemSizes.object(index); +} + +QPair CategorizedView::categoryInternalPosition(const QModelIndex &index) const +{ + QList indices = itemsForCategory(category(index)); + int x = 0; + int y = 0; + const int perRow = itemsPerRow(); + for (int i = 0; i < indices.size(); ++i) + { + if (indices.at(i) == index) + { + break; + } + ++x; + if (x == perRow) + { + x = 0; + ++y; } } - return m_cachedItemSize; + return qMakePair(x, y); } void CategorizedView::mousePressEvent(QMouseEvent *event) @@ -719,25 +770,11 @@ QRect CategorizedView::visualRect(const QModelIndex &index) const if (!m_cachedVisualRects.contains(index)) { const Category *cat = category(index); - QList indices = itemsForCategory(cat); - int x = 0; - int y = 0; - const int perRow = itemsPerRow(); - for (int i = 0; i < indices.size(); ++i) - { - if (indices.at(i) == index) - { - break; - } - ++x; - if (x == perRow) - { - x = 0; - ++y; - } - } + QPair pos = categoryInternalPosition(index); + int x = pos.first; + int y = pos.second; - QSize size = itemSize(); + QSize size = itemSize(index); QRect *out = new QRect; out->setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height()); @@ -897,7 +934,7 @@ QPair CategorizedView::rowDropPos(const QPoint // calculate the internal column int internalColumn = -1; { - const int itemWidth = itemSize().width(); + const int itemWidth = this->itemWidth(); for (int i = 0, c = 0; i < contentWidth(); i += itemWidth, ++c) @@ -918,7 +955,8 @@ QPair CategorizedView::rowDropPos(const QPoint // calculate the internal row int internalRow = -1; { - const int itemHeight = itemSize().height(); + // FIXME rework the drag and drop code + const int itemHeight = 0; //itemSize().height(); const int top = categoryTop(category); for (int i = top + category->headerHeight(), r = 0; i < top + category->totalHeight(); diff --git a/CategorizedView.h b/CategorizedView.h index 0756629a..e98e7c5e 100644 --- a/CategorizedView.h +++ b/CategorizedView.h @@ -91,9 +91,10 @@ private: QList sortedCategories() const; private: - mutable QSize m_cachedItemSize; - QSize itemSize(const QStyleOptionViewItem &option) const; - QSize itemSize() const { return itemSize(viewOptions()); } + mutable int m_cachedItemWidth; + mutable QCache m_cachedItemSizes; + int itemWidth() const; + QSize itemSize(const QModelIndex &index) const; /*QLineEdit *m_categoryEditor; Category *m_editedCategory; @@ -110,6 +111,8 @@ private: QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; QPoint m_lastDragPosition; + QPair categoryInternalPosition(const QModelIndex &index) const; + QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; QList > draggablePaintPairs(const QModelIndexList &indices, QRect *r) const; diff --git a/InstanceDelegate.cpp b/InstanceDelegate.cpp new file mode 100644 index 00000000..5020b8b6 --- /dev/null +++ b/InstanceDelegate.cpp @@ -0,0 +1,254 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 "InstanceDelegate.h" +#include +#include +#include +#include +#include + +// Origin: Qt +static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, + qreal &widthUsed) +{ + height = 0; + widthUsed = 0; + textLayout.beginLayout(); + QString str = textLayout.text(); + while (true) + { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + if (line.textLength() == 0) + break; + line.setLineWidth(lineWidth); + line.setPosition(QPointF(0, height)); + height += line.height(); + widthUsed = qMax(widthUsed, line.naturalTextWidth()); + } + textLayout.endLayout(); +} + +#define QFIXED_MAX (INT_MAX / 256) + +ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, + const QRect &rect) +{ + 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) +{ + if (!(option.state & QStyle::State_HasFocus)) + return; + QStyleOptionFocusRect opt; + opt.direction = option.direction; + opt.fontMetrics = option.fontMetrics; + opt.palette = option.palette; + opt.rect = rect; + // opt.state = option.state | QStyle::State_KeyboardFocusChange | + // QStyle::State_Item; + auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base; + opt.backgroundColor = option.palette.color(col); + // Apparently some widget styles expect this hint to not be set + painter->setRenderHint(QPainter::Antialiasing, false); + + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + + style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); + + painter->setRenderHint(QPainter::Antialiasing); +} + +static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option) +{ + QStyle *style = option->widget ? option->widget->style() : QApplication::style(); + QTextOption textOption; + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + QTextLayout textLayout; + textLayout.setTextOption(textOption); + textLayout.setFont(option->font); + textLayout.setText(option->text); + const int textMargin = + style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1; + QRect bounds(0, 0, 100 - 2 * textMargin, 600); + qreal height = 0, widthUsed = 0; + viewItemTextLayout(textLayout, bounds.width(), height, widthUsed); + const QSize size(qCeil(widthUsed), qCeil(height)); + return QSize(size.width() + 2 * textMargin, size.height()); +} + +void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + painter->save(); + painter->setClipRect(opt.rect); + + opt.features |= QStyleOptionViewItem::WrapText; + opt.text = index.data().toString(); + opt.textElideMode = Qt::ElideRight; + opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; + + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + + // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); + const int iconSize = 48; + QRect iconbox = opt.rect; + const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; + QRect textRect = opt.rect; + QRect textHighlightRect = textRect; + // clip the decoration on top, remove width padding + textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0); + + textHighlightRect.adjust(0, iconSize + 5, 0, 0); + + // draw background + { + // FIXME: unused + // QSize textSize = viewItemTextSize ( &opt ); + QPalette::ColorGroup cg; + QStyleOptionViewItemV4 opt2(opt); + + if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) + { + if (!(opt.state & QStyle::State_Active)) + cg = QPalette::Inactive; + else + cg = QPalette::Normal; + } + else + { + cg = QPalette::Disabled; + } + opt2.palette.setCurrentColorGroup(cg); + + // fill in background, if any + if (opt.backgroundBrush.style() != Qt::NoBrush) + { + QPointF oldBO = painter->brushOrigin(); + painter->setBrushOrigin(opt.rect.topLeft()); + painter->fillRect(opt.rect, opt.backgroundBrush); + painter->setBrushOrigin(oldBO); + } + + if (opt.showDecorationSelected) + { + drawSelectionRect(painter, opt2, opt.rect); + drawFocusRect(painter, opt2, opt.rect); + // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); + } + else + { + + // if ( opt.state & QStyle::State_Selected ) + { + // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, + // opt.widget ); + // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, + // QPalette::Highlight ) ); + drawSelectionRect(painter, opt2, textHighlightRect); + drawFocusRect(painter, opt2, textHighlightRect); + } + } + } + + // draw the icon + { + QIcon::Mode mode = QIcon::Normal; + if (!(opt.state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (opt.state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; + + iconbox.setHeight(iconSize); + opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); + } + // set the text colors + QPalette::ColorGroup cg = + opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) + cg = QPalette::Inactive; + if (opt.state & QStyle::State_Selected) + { + painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); + } + else + { + painter->setPen(opt.palette.color(cg, QPalette::Text)); + } + + // draw the text + QTextOption textOption; + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + textOption.setTextDirection(opt.direction); + textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment)); + QTextLayout textLayout; + textLayout.setTextOption(textOption); + textLayout.setFont(opt.font); + textLayout.setText(opt.text); + + qreal width, height; + viewItemTextLayout(textLayout, textRect.width(), height, width); + + const int lineCount = textLayout.lineCount(); + + const QRect layoutRect = QStyle::alignedRect( + opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); + const QPointF position = layoutRect.topLeft(); + for (int i = 0; i < lineCount; ++i) + { + const QTextLine line = textLayout.lineAt(i); + line.draw(painter, position); + } + + painter->restore(); +} + +QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + opt.features |= QStyleOptionViewItem::WrapText; + opt.text = index.data().toString(); + opt.textElideMode = Qt::ElideRight; + opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; + + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + const int textMargin = + style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; + int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables + QSize szz = viewItemTextSize(&opt); + height += szz.height(); + // FIXME: maybe the icon items could scale and keep proportions? + QSize sz(100, height); + return sz; +} diff --git a/InstanceDelegate.h b/InstanceDelegate.h new file mode 100644 index 00000000..6f924405 --- /dev/null +++ b/InstanceDelegate.h @@ -0,0 +1,27 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 + +class ListViewDelegate : public QStyledItemDelegate +{ +public: + explicit ListViewDelegate ( QObject* parent = 0 ); +protected: + void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; +}; diff --git a/main.cpp b/main.cpp index 427ae17a..b164b85d 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ #include #include "CategorizedProxyModel.h" +#include "InstanceDelegate.h" QPixmap icon(const Qt::GlobalColor color) { @@ -50,7 +51,7 @@ int main(int argc, char *argv[]) model.setRowCount(10); model.setColumnCount(1); - model.setItem(0, createItem(Qt::red, "Red", "Colorful")); + model.setItem(0, createItem(Qt::red, "Red is a color. Some more text. I'm out of ideas. 42. What's your name?", "Colorful")); model.setItem(1, createItem(Qt::blue, "Blue", "Colorful")); model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful")); @@ -72,6 +73,7 @@ int main(int argc, char *argv[]) pModel.setSourceModel(&model); CategorizedView w; + w.setItemDelegate(new ListViewDelegate); w.setModel(&pModel); w.resize(640, 480); w.show(); From 1e1b2342f48a3dd1eff90229ac4005fb0d45d2ba Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 30 Dec 2013 18:46:12 +0100 Subject: [PATCH 09/49] Loads of fixes --- CategorizedView.cpp | 115 ++++++++++++++++++++++++++------------------ CategorizedView.h | 8 ++- main.cpp | 2 +- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index bdb0b222..f660e8f3 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -79,7 +79,6 @@ int CategorizedView::Category::contentHeight() const { return 0; } - const int rows = qMax(1, qCeil((qreal)view->numItemsForCategory(this) / (qreal)view->itemsPerRow())); QMap rowToHeightMapping; foreach (const QModelIndex &index, view->itemsForCategory(this)) { @@ -90,20 +89,23 @@ int CategorizedView::Category::contentHeight() const } } int result = 0; - for (int i = 0; i < rows; ++i) + if (!rowToHeightMapping.isEmpty()) { - Q_ASSERT(rowToHeightMapping.contains(i)); - result += rowToHeightMapping[i]; + for (int i = 0; i < numRows(); ++i) + { + Q_ASSERT(rowToHeightMapping.contains(i)); + result += rowToHeightMapping[i]; + } } return result; } -QSize CategorizedView::Category::categoryTotalSize() const +int CategorizedView::Category::numRows() const { - return QSize(view->contentWidth(), contentHeight()); + return qMax(1, qCeil((qreal)view->numItemsForCategory(this) / (qreal)view->itemsPerRow())); } CategorizedView::CategorizedView(QWidget *parent) - : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_categoryMargin(5)//, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) + : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), m_categoryMargin(5)//, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) { setViewMode(IconMode); setMovement(Snap); @@ -168,10 +170,7 @@ void CategorizedView::updateGeometries() { QListView::updateGeometries(); - m_cachedItemWidth = -1; - m_cachedCategoryToIndexMapping.clear(); - m_cachedVisualRects.clear(); - m_cachedItemSizes.clear(); + invalidateCaches(); QMap cats; @@ -213,6 +212,7 @@ void CategorizedView::updateGeometries() } // remove the last margin (we don't want it) totalHeight -= m_categoryMargin; + totalHeight += m_bottomMargin; verticalScrollBar()->setRange(0, totalHeight- height()); } @@ -265,7 +265,7 @@ int CategorizedView::numItemsForCategory(const CategorizedView::Category *catego } QList CategorizedView::itemsForCategory(const CategorizedView::Category *category) const { - if (!m_cachedCategoryToIndexMapping.contains(category)) + if (!m_cachedCategoryToIndexMapping.contains(category) || true) { QList *indices = new QList(); for (int i = 0; i < model()->rowCount(); ++i) @@ -393,26 +393,30 @@ QPair CategorizedView::categoryInternalPosition(const QModelIndex &ind } return qMakePair(x, y); } +int CategorizedView::itemHeightForCategoryRow(const CategorizedView::Category *category, const int internalRow) const +{ + foreach (const QModelIndex &i, itemsForCategory(category)) + { + QPair pos = categoryInternalPosition(i); + if (pos.second == internalRow) + { + return itemSize(i).height(); + } + } + return -1; +} void CategorizedView::mousePressEvent(QMouseEvent *event) { //endCategoryEditor(); - QPoint pos = event->pos(); + QPoint pos = event->pos() + offset(); QPersistentModelIndex index = indexAt(pos); m_pressedIndex = index; m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); - QPoint offset = QPoint(horizontalOffset(), verticalOffset()); - if (!(command & QItemSelectionModel::Current)) - { - m_pressedPosition = pos + offset; - } - else if (!indexAt(m_pressedPosition - offset).isValid()) - { - m_pressedPosition = visualRect(currentIndex()).center() + offset; - } + m_pressedPosition = pos; m_pressedCategory = categoryAt(m_pressedPosition); if (m_pressedCategory) @@ -430,7 +434,7 @@ void CategorizedView::mousePressEvent(QMouseEvent *event) setAutoScroll(false); selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); setAutoScroll(autoScroll); - QRect rect(m_pressedPosition - offset, pos); + QRect rect(m_pressedPosition, pos); if (command.testFlag(QItemSelectionModel::Toggle)) { command &= ~QItemSelectionModel::Toggle; @@ -459,7 +463,7 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) if (state() == DraggingState) { - topLeft = m_pressedPosition - QPoint(horizontalOffset(), verticalOffset()); + topLeft = m_pressedPosition - offset(); if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) { m_pressedIndex = QModelIndex(); @@ -474,7 +478,7 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) if (selectionMode() != SingleSelection) { - topLeft = m_pressedPosition - QPoint(horizontalOffset(), verticalOffset()); + topLeft = m_pressedPosition - offset(); } else { @@ -515,7 +519,7 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) } void CategorizedView::mouseReleaseEvent(QMouseEvent *event) { - QPoint pos = event->pos(); + QPoint pos = event->pos() - offset(); QPersistentModelIndex index = indexAt(pos); bool click = (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(pos)); @@ -582,8 +586,10 @@ void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) void CategorizedView::paintEvent(QPaintEvent *event) { QPainter painter(this->viewport()); - QPoint offset(horizontalOffset(), verticalOffset()); - painter.translate(-offset); + painter.translate(-offset()); + + // FIXME we shouldn't need to do this + invalidateCaches(); int y = 0; for (int i = 0; i < m_categories.size(); ++i) @@ -665,7 +671,7 @@ void CategorizedView::dragEnterEvent(QDragEnterEvent *event) { return; } - m_lastDragPosition = event->pos(); + m_lastDragPosition = event->pos() + offset(); viewport()->update(); event->accept(); } @@ -675,7 +681,7 @@ void CategorizedView::dragMoveEvent(QDragMoveEvent *event) { return; } - m_lastDragPosition = event->pos(); + m_lastDragPosition = event->pos() + offset(); viewport()->update(); event->accept(); } @@ -696,7 +702,7 @@ void CategorizedView::dropEvent(QDropEvent *event) return; } - QPair dropPos = rowDropPos(event->pos()); + QPair dropPos = rowDropPos(event->pos() + offset()); const Category *category = dropPos.first; const int row = dropPos.second; @@ -729,7 +735,8 @@ void CategorizedView::startDrag(Qt::DropActions supportedActions) } QRect rect; QPixmap pixmap = renderToPixmap(indexes, &rect); - rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); + rect.translate(offset()); + //rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); @@ -935,15 +942,22 @@ QPair CategorizedView::rowDropPos(const QPoint int internalColumn = -1; { const int itemWidth = this->itemWidth(); - for (int i = 0, c = 0; - i < contentWidth(); - i += itemWidth, ++c) + if (pos.x() >= (itemWidth * itemsPerRow())) { - if (pos.x() > (i - itemWidth / 2) && - pos.x() < (i + itemWidth / 2)) + internalColumn = itemsPerRow(); + } + else + { + for (int i = 0, c = 0; + i < contentWidth(); + i += itemWidth + spacing(), ++c) { - internalColumn = c; - break; + if (pos.x() > (i - itemWidth / 2) && + pos.x() <= (i + itemWidth / 2)) + { + internalColumn = c; + break; + } } } if (internalColumn == -1) @@ -956,13 +970,10 @@ QPair CategorizedView::rowDropPos(const QPoint int internalRow = -1; { // FIXME rework the drag and drop code - const int itemHeight = 0; //itemSize().height(); const int top = categoryTop(category); - for (int i = top + category->headerHeight(), r = 0; - i < top + category->totalHeight(); - i += itemHeight, ++r) + for (int r = 0, h = top; r < category->numRows(); h += itemHeightForCategoryRow(category, r), ++r) { - if (pos.y() > i && pos.y() < (i + itemHeight)) + if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r))) { internalRow = r; break; @@ -984,11 +995,23 @@ QPair CategorizedView::rowDropPos(const QPoint int categoryRow = internalRow * itemsPerRow() + internalColumn; // this is used if we're past the last item - int numItemsInLastRow = indices.size() % itemsPerRow(); - if (internalColumn >= numItemsInLastRow) + if (categoryRow >= indices.size()) { return qMakePair(category, indices.last().row() + 1); } return qMakePair(category, indices.at(categoryRow).row()); } + +void CategorizedView::invalidateCaches() +{ + m_cachedItemWidth = -1; + m_cachedCategoryToIndexMapping.clear(); + m_cachedVisualRects.clear(); + m_cachedItemSizes.clear(); +} + +QPoint CategorizedView::offset() const +{ + return QPoint(horizontalOffset(), verticalOffset()); +} diff --git a/CategorizedView.h b/CategorizedView.h index e98e7c5e..8ab9ce87 100644 --- a/CategorizedView.h +++ b/CategorizedView.h @@ -59,7 +59,7 @@ private: int totalHeight() const; int headerHeight() const; int contentHeight() const; - QSize categoryTotalSize() const; + int numRows() const; }; friend struct Category; @@ -69,6 +69,7 @@ private: int m_leftMargin; int m_rightMargin; + int m_bottomMargin; int m_categoryMargin; int m_itemSpacing; @@ -112,6 +113,7 @@ private: QPoint m_lastDragPosition; QPair categoryInternalPosition(const QModelIndex &index) const; + int itemHeightForCategoryRow(const Category *category, const int internalRow) const; QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; QList > draggablePaintPairs(const QModelIndexList &indices, QRect *r) const; @@ -119,6 +121,10 @@ private: bool isDragEventAccepted(QDropEvent *event); QPair rowDropPos(const QPoint &pos); + + void invalidateCaches(); + + QPoint offset() const; }; #endif // WIDGET_H diff --git a/main.cpp b/main.cpp index b164b85d..58d1c9ba 100644 --- a/main.cpp +++ b/main.cpp @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) model.setItem(8, createItem(Qt::darkGreen, "Dark Green", "")); model.setItem(9, createItem(Qt::green, "Green", "")); - for (int i = 0; i < 21; ++i) + for (int i = 0; i < 20; ++i) { model.setItem(i + 10, createItem(i+1, "Items 1-20")); } From e6be883d14a4147ffd58a1c8066bb70879d775fb Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 30 Dec 2013 23:10:53 +0100 Subject: [PATCH 10/49] Fixing more bugs --- CategorizedView.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/CategorizedView.cpp b/CategorizedView.cpp index f660e8f3..27575b59 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -170,6 +170,8 @@ void CategorizedView::updateGeometries() { QListView::updateGeometries(); + int previousScroll = verticalScrollBar()->value(); + invalidateCaches(); QMap cats; @@ -216,6 +218,8 @@ void CategorizedView::updateGeometries() verticalScrollBar()->setRange(0, totalHeight- height()); } + verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum())); + update(); } @@ -435,13 +439,7 @@ void CategorizedView::mousePressEvent(QMouseEvent *event) selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); setAutoScroll(autoScroll); QRect rect(m_pressedPosition, pos); - if (command.testFlag(QItemSelectionModel::Toggle)) - { - command &= ~QItemSelectionModel::Toggle; - m_ctrlDragSelectionFlag = selectionModel()->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; - command |= m_ctrlDragSelectionFlag; - } - setSelection(rect, command); + setSelection(rect, QItemSelectionModel::ClearAndSelect); // signal handlers may change the model emit pressed(index); @@ -519,7 +517,7 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) } void CategorizedView::mouseReleaseEvent(QMouseEvent *event) { - QPoint pos = event->pos() - offset(); + QPoint pos = event->pos() + offset(); QPersistentModelIndex index = indexAt(pos); bool click = (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(pos)); @@ -611,7 +609,7 @@ void CategorizedView::paintEvent(QPaintEvent *event) option.rect = visualRect(index); option.widget = this; option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText : QStyleOptionViewItemV2::None; - if (flags & Qt::ItemIsSelectable) + if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) { option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected : QStyle::State_None; } @@ -844,16 +842,15 @@ QModelIndex CategorizedView::indexAt(const QPoint &point) const } void CategorizedView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) { - QItemSelection selection; for (int i = 0; i < model()->rowCount(); ++i) { QModelIndex index = model()->index(i, 0); if (visualRect(index).intersects(rect)) { - selection.merge(QItemSelection(index, index), QItemSelectionModel::Select); + selectionModel()->select(index, commands); } } - selectionModel()->select(selection, commands); + update(); } QPixmap CategorizedView::renderToPixmap(const QModelIndexList &indices, QRect *r) const From 8cfd0881ac3fcbb45fd42dedcb1caf0be38eacaf Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 30 Dec 2013 23:39:10 +0100 Subject: [PATCH 11/49] Progress indicators --- CMakeLists.txt | 1 + CategorizedProxyModel.cpp | 4 +-- CategorizedView.cpp | 10 ++++---- CategorizedView.h | 15 +++++++---- InstanceDelegate.cpp | 25 ++++++++++++++++++ main.cpp | 19 +++++++++++--- main.h | 53 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 main.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b94cf53e..029c90b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_ set(SOURCES main.cpp + main.h CategorizedView.h CategorizedView.cpp diff --git a/CategorizedProxyModel.cpp b/CategorizedProxyModel.cpp index e4a7563a..efcf13c8 100644 --- a/CategorizedProxyModel.cpp +++ b/CategorizedProxyModel.cpp @@ -8,8 +8,8 @@ CategorizedProxyModel::CategorizedProxyModel(QObject *parent) } bool CategorizedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - const QString leftCategory = left.data(CategorizedView::CategoryRole).toString(); - const QString rightCategory = right.data(CategorizedView::CategoryRole).toString(); + const QString leftCategory = left.data(CategorizedViewRoles::CategoryRole).toString(); + const QString rightCategory = right.data(CategorizedViewRoles::CategoryRole).toString(); if (leftCategory == rightCategory) { return left.row() < right.row(); diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 27575b59..6bf18cbc 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -135,7 +135,7 @@ void CategorizedView::dataChanged(const QModelIndex &topLeft, const QModelIndex QListView::dataChanged(topLeft, bottomRight, roles); - if (roles.contains(CategoryRole)) + if (roles.contains(CategorizedViewRoles::CategoryRole) || roles.contains(Qt::DisplayRole)) { updateGeometries(); update(); @@ -178,7 +178,7 @@ void CategorizedView::updateGeometries() for (int i = 0; i < model()->rowCount(); ++i) { - const QString category = model()->index(i, 0).data(CategoryRole).toString(); + const QString category = model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString(); if (!cats.contains(category)) { Category *old = this->category(category); @@ -238,7 +238,7 @@ bool CategorizedView::isIndexHidden(const QModelIndex &index) const CategorizedView::Category *CategorizedView::category(const QModelIndex &index) const { - return category(index.data(CategoryRole).toString()); + return category(index.data(CategorizedViewRoles::CategoryRole).toString()); } CategorizedView::Category *CategorizedView::category(const QString &cat) const { @@ -274,7 +274,7 @@ QList CategorizedView::itemsForCategory(const CategorizedView::Cate QList *indices = new QList(); for (int i = 0; i < model()->rowCount(); ++i) { - if (model()->index(i, 0).data(CategoryRole).toString() == category->text) + if (model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString() == category->text) { indices->append(model()->index(i, 0)); } @@ -713,7 +713,7 @@ void CategorizedView::dropEvent(QDropEvent *event) const QString categoryText = category->text; if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) { - model()->setData(model()->index(row, 0), categoryText, CategoryRole); + model()->setData(model()->index(row, 0), categoryText, CategorizedViewRoles::CategoryRole); event->setDropAction(Qt::MoveAction); event->accept(); } diff --git a/CategorizedView.h b/CategorizedView.h index 8ab9ce87..08d43be8 100644 --- a/CategorizedView.h +++ b/CategorizedView.h @@ -5,6 +5,16 @@ #include #include +struct CategorizedViewRoles +{ + enum + { + CategoryRole = Qt::UserRole, + ProgressValueRole, + ProgressMaximumRole + }; +}; + class CategorizedView : public QListView { Q_OBJECT @@ -13,11 +23,6 @@ public: CategorizedView(QWidget *parent = 0); ~CategorizedView(); - enum - { - CategoryRole = Qt::UserRole - }; - virtual QRect visualRect(const QModelIndex &index) const; QModelIndex indexAt(const QPoint &point) const; void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; diff --git a/InstanceDelegate.cpp b/InstanceDelegate.cpp index 5020b8b6..50cead55 100644 --- a/InstanceDelegate.cpp +++ b/InstanceDelegate.cpp @@ -20,6 +20,8 @@ #include #include +#include "CategorizedView.h" + // Origin: Qt static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, qreal &widthUsed) @@ -85,6 +87,26 @@ void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, cons painter->setRenderHint(QPainter::Antialiasing); } +// TODO this can be made a lot prettier +void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option, const int value, const int maximum) +{ + if (maximum == 0 || value == maximum) + { + return; + } + + painter->save(); + + qreal percent = (qreal)value / (qreal)maximum; + QColor color = option.palette.color(QPalette::Dark); + color.setAlphaF(0.70f); + painter->setBrush(color); + painter->setPen(QPen(QBrush(), 0)); + painter->drawPie(option.rect, 90 * 16, -percent * 360 * 60); + + painter->restore(); +} + static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option) { QStyle *style = option->widget ? option->widget->style() : QApplication::style(); @@ -229,6 +251,9 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti line.draw(painter, position); } + drawProgressOverlay(painter, opt, index.data(CategorizedViewRoles::ProgressValueRole).toInt(), + index.data(CategorizedViewRoles::ProgressMaximumRole).toInt()); + painter->restore(); } diff --git a/main.cpp b/main.cpp index 58d1c9ba..bd6a44f9 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,16 @@ -#include "CategorizedView.h" +#include "main.h" + #include #include #include +#include +#include "CategorizedView.h" #include "CategorizedProxyModel.h" #include "InstanceDelegate.h" +Progresser *progresser; + QPixmap icon(const Qt::GlobalColor color) { QPixmap p = QPixmap(32, 32); @@ -29,8 +34,9 @@ QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, cons QStandardItem *item = new QStandardItem; item->setText(text); item->setData(icon(color), Qt::DecorationRole); - item->setData(category, CategorizedView::CategoryRole); + item->setData(category, CategorizedViewRoles::CategoryRole); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + //progresser->addTrackedIndex(item); return item; } QStandardItem *createItem(const int index, const QString &category) @@ -38,8 +44,9 @@ QStandardItem *createItem(const int index, const QString &category) QStandardItem *item = new QStandardItem; item->setText(QString("Item #%1").arg(index)); item->setData(icon(index), Qt::DecorationRole); - item->setData(category, CategorizedView::CategoryRole); + item->setData(category, CategorizedViewRoles::CategoryRole); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + //progresser->addTrackedIndex(item); return item; } @@ -47,6 +54,10 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); + qsrand(QTime::currentTime().msec()); + + progresser = new Progresser(); + QStandardItemModel model; model.setRowCount(10); model.setColumnCount(1); @@ -62,7 +73,7 @@ int main(int argc, char *argv[]) model.setItem(7, createItem(Qt::white, "White", "Not Colorful")); model.setItem(8, createItem(Qt::darkGreen, "Dark Green", "")); - model.setItem(9, createItem(Qt::green, "Green", "")); + model.setItem(9, progresser->addTrackedIndex(createItem(Qt::green, "Green", ""))); for (int i = 0; i < 20; ++i) { diff --git a/main.h b/main.h new file mode 100644 index 00000000..f4c7a3f8 --- /dev/null +++ b/main.h @@ -0,0 +1,53 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include +#include +#include + +#include "CategorizedView.h" + +class Progresser : public QObject +{ + Q_OBJECT +public: + explicit Progresser(QObject *parent = 0) : QObject(parent) + { + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); + timer->start(50); + } + + QStandardItem *addTrackedIndex(QStandardItem *item) + { + item->setData(1000, CategorizedViewRoles::ProgressMaximumRole); + m_items.append(item); + return item; + } + +public slots: + void timeout() + { + foreach (QStandardItem *item, m_items) + { + int value = item->data(CategorizedViewRoles::ProgressValueRole).toInt(); + value += qrand() % 3; + if (value >= item->data(CategorizedViewRoles::ProgressMaximumRole).toInt()) + { + item->setData(item->data(CategorizedViewRoles::ProgressMaximumRole).toInt(), + CategorizedViewRoles::ProgressValueRole); + } + else + { + item->setData(value, CategorizedViewRoles::ProgressValueRole); + } + } + } + +private: + QList m_items; +}; + +#endif // MAIN_H From c47933d95cae407a99acfbbb941655f7cd52e1ae Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Tue, 31 Dec 2013 17:26:36 +0100 Subject: [PATCH 12/49] Loads of changes and some refactorings --- CMakeLists.txt | 2 + CategorizedView.cpp | 298 ++++++++---------------------------- CategorizedView.h | 56 ++----- CategorizedViewCategory.cpp | 128 ++++++++++++++++ CategorizedViewCategory.h | 39 +++++ 5 files changed, 249 insertions(+), 274 deletions(-) create mode 100644 CategorizedViewCategory.cpp create mode 100644 CategorizedViewCategory.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 029c90b3..44a28c57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ set(SOURCES CategorizedView.h CategorizedView.cpp + CategorizedViewCategory.h + CategorizedViewCategory.cpp CategorizedProxyModel.h CategorizedProxyModel.cpp InstanceDelegate.h diff --git a/CategorizedView.cpp b/CategorizedView.cpp index 6bf18cbc..60230661 100644 --- a/CategorizedView.cpp +++ b/CategorizedView.cpp @@ -11,6 +11,8 @@ #include #include +#include "CategorizedViewCategory.h" + template bool listsIntersect(const QList &l1, const QList t2) { @@ -24,86 +26,6 @@ bool listsIntersect(const QList &l1, const QList t2) return false; } -CategorizedView::Category::Category(const QString &text, CategorizedView *view) - : view(view), text(text), collapsed(false) -{ -} -CategorizedView::Category::Category(const CategorizedView::Category *other) : - view(other->view), text(other->text), collapsed(other->collapsed), iconRect(other->iconRect), textRect(other->textRect) -{ -} - -void CategorizedView::Category::drawHeader(QPainter *painter, const int y) -{ - painter->save(); - - int height = headerHeight() - 4; - int collapseSize = height; - - // the icon - iconRect = QRect(view->m_rightMargin + 2, 2 + y, collapseSize, collapseSize); - painter->setPen(QPen(Qt::black, 1)); - painter->drawRect(iconRect); - static const int margin = 2; - QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); - int midX = iconSubrect.center().x(); - int midY = iconSubrect.center().y(); - if (collapsed) - { - painter->drawLine(midX, iconSubrect.top(), midX, iconSubrect.bottom()); - } - painter->drawLine(iconSubrect.left(), midY, iconSubrect.right(), midY); - - // the text - int textWidth = painter->fontMetrics().width(text); - textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); - view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, view->palette(), true, text); - - // the line - painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); - - painter->restore(); -} - -int CategorizedView::Category::totalHeight() const -{ - return headerHeight() + 5 + contentHeight(); -} -int CategorizedView::Category::headerHeight() const -{ - return qApp->fontMetrics().height() + 4; -} -int CategorizedView::Category::contentHeight() const -{ - if (collapsed) - { - return 0; - } - QMap rowToHeightMapping; - foreach (const QModelIndex &index, view->itemsForCategory(this)) - { - int row = view->categoryInternalPosition(index).second; - if (!rowToHeightMapping.contains(row)) - { - rowToHeightMapping.insert(row, view->itemSize(index).height()); - } - } - int result = 0; - if (!rowToHeightMapping.isEmpty()) - { - for (int i = 0; i < numRows(); ++i) - { - Q_ASSERT(rowToHeightMapping.contains(i)); - result += rowToHeightMapping[i]; - } - } - return result; -} -int CategorizedView::Category::numRows() const -{ - return qMax(1, qCeil((qreal)view->numItemsForCategory(this) / (qreal)view->itemsPerRow())); -} - CategorizedView::CategorizedView(QWidget *parent) : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), m_categoryMargin(5)//, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) { @@ -115,9 +37,6 @@ CategorizedView::CategorizedView(QWidget *parent) setDragDropMode(QListView::InternalMove); setAcceptDrops(true); setSpacing(10); - - m_cachedCategoryToIndexMapping.setMaxCost(50); - m_cachedVisualRects.setMaxCost(50); } CategorizedView::~CategorizedView() @@ -172,23 +91,21 @@ void CategorizedView::updateGeometries() int previousScroll = verticalScrollBar()->value(); - invalidateCaches(); - - QMap cats; + QMap cats; for (int i = 0; i < model()->rowCount(); ++i) { const QString category = model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString(); if (!cats.contains(category)) { - Category *old = this->category(category); + CategorizedViewCategory *old = this->category(category); if (old) { - cats.insert(category, new Category(old)); + cats.insert(category, new CategorizedViewCategory(old)); } else { - cats.insert(category, new Category(category, this)); + cats.insert(category, new CategorizedViewCategory(category, this)); } } } @@ -201,6 +118,11 @@ void CategorizedView::updateGeometries() qDeleteAll(m_categories); m_categories = cats.values(); + for (auto cat : m_categories) + { + cat->update(); + } + if (m_categories.isEmpty()) { verticalScrollBar()->setRange(0, 0); @@ -208,7 +130,7 @@ void CategorizedView::updateGeometries() else { int totalHeight = 0; - foreach (const Category *category, m_categories) + foreach (const CategorizedViewCategory *category, m_categories) { totalHeight += category->totalHeight() + m_categoryMargin; } @@ -225,7 +147,7 @@ void CategorizedView::updateGeometries() bool CategorizedView::isIndexHidden(const QModelIndex &index) const { - Category *cat = category(index); + CategorizedViewCategory *cat = category(index); if (cat) { return cat->collapsed; @@ -236,11 +158,11 @@ bool CategorizedView::isIndexHidden(const QModelIndex &index) const } } -CategorizedView::Category *CategorizedView::category(const QModelIndex &index) const +CategorizedViewCategory *CategorizedView::category(const QModelIndex &index) const { return category(index.data(CategorizedViewRoles::CategoryRole).toString()); } -CategorizedView::Category *CategorizedView::category(const QString &cat) const +CategorizedViewCategory *CategorizedView::category(const QString &cat) const { for (int i = 0; i < m_categories.size(); ++i) { @@ -251,7 +173,7 @@ CategorizedView::Category *CategorizedView::category(const QString &cat) const } return 0; } -CategorizedView::Category *CategorizedView::categoryAt(const QPoint &pos) const +CategorizedViewCategory *CategorizedView::categoryAt(const QPoint &pos) const { for (int i = 0; i < m_categories.size(); ++i) { @@ -263,70 +185,6 @@ CategorizedView::Category *CategorizedView::categoryAt(const QPoint &pos) const return 0; } -int CategorizedView::numItemsForCategory(const CategorizedView::Category *category) const -{ - return itemsForCategory(category).size(); -} -QList CategorizedView::itemsForCategory(const CategorizedView::Category *category) const -{ - if (!m_cachedCategoryToIndexMapping.contains(category) || true) - { - QList *indices = new QList(); - for (int i = 0; i < model()->rowCount(); ++i) - { - if (model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString() == category->text) - { - indices->append(model()->index(i, 0)); - } - } - m_cachedCategoryToIndexMapping.insert(category, indices, indices->size()); - } - return *m_cachedCategoryToIndexMapping.object(category); -} -QModelIndex CategorizedView::firstItemForCategory(const CategorizedView::Category *category) const -{ - QList indices = itemsForCategory(category); - QModelIndex first; - foreach (const QModelIndex &index, indices) - { - if (index.row() < first.row() || !first.isValid()) - { - first = index; - } - } - - return first; -} -QModelIndex CategorizedView::lastItemForCategory(const CategorizedView::Category *category) const -{ - QList indices = itemsForCategory(category); - QModelIndex last; - foreach (const QModelIndex &index, indices) - { - if (index.row() > last.row() || !last.isValid()) - { - last = index; - } - } - - return last; -} - -int CategorizedView::categoryTop(const CategorizedView::Category *category) const -{ - int res = 0; - const QList cats = sortedCategories(); - for (int i = 0; i < cats.size(); ++i) - { - if (cats.at(i) == category) - { - break; - } - res += cats.at(i)->totalHeight() + m_categoryMargin; - } - return res; -} - int CategorizedView::itemsPerRow() const { return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + spacing())); @@ -336,49 +194,34 @@ int CategorizedView::contentWidth() const return width() - m_leftMargin - m_rightMargin; } -QList CategorizedView::sortedCategories() const -{ - QList out = m_categories; - qSort(out.begin(), out.end(), [](const Category *c1, const Category *c2) { return c1->text < c2->text; }); - return out; -} - int CategorizedView::itemWidth() const { - if (m_cachedItemWidth == -1) - { - m_cachedItemWidth = itemDelegate()->sizeHint(viewOptions(), model()->index(model()->rowCount() -1, 0)).width(); - } - return m_cachedItemWidth; + return itemDelegate()->sizeHint(viewOptions(), model()->index(model()->rowCount() -1, 0)).width(); } -QSize CategorizedView::itemSize(const QModelIndex &index) const +int CategorizedView::categoryRowHeight(const QModelIndex &index) const { - if (!m_cachedItemSizes.contains(index)) + QModelIndexList indices; + int internalRow = categoryInternalPosition(index).second; + foreach (const QModelIndex &i, category(index)->items()) { - QModelIndexList indices; - int internalRow = categoryInternalPosition(index).second; - foreach (const QModelIndex &i, itemsForCategory(category(index))) + if (categoryInternalPosition(i).second == internalRow) { - if (categoryInternalPosition(i).second == internalRow) - { - indices.append(i); - } + indices.append(i); } - - int largestHeight = 0; - foreach (const QModelIndex &i, indices) - { - largestHeight = qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); - } - m_cachedItemSizes.insert(index, new QSize(itemWidth(), largestHeight)); } - return *m_cachedItemSizes.object(index); + + int largestHeight = 0; + foreach (const QModelIndex &i, indices) + { + largestHeight = qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); + } + return largestHeight; } QPair CategorizedView::categoryInternalPosition(const QModelIndex &index) const { - QList indices = itemsForCategory(category(index)); + QList indices = category(index)->items(); int x = 0; int y = 0; const int perRow = itemsPerRow(); @@ -397,14 +240,25 @@ QPair CategorizedView::categoryInternalPosition(const QModelIndex &ind } return qMakePair(x, y); } -int CategorizedView::itemHeightForCategoryRow(const CategorizedView::Category *category, const int internalRow) const +int CategorizedView::categoryInternalRowTop(const QModelIndex &index) const { - foreach (const QModelIndex &i, itemsForCategory(category)) + CategorizedViewCategory *cat = category(index); + int categoryInternalRow = categoryInternalPosition(index).second; + int result = 0; + for (int i = 0; i < categoryInternalRow; ++i) + { + result += cat->rowHeights.at(i); + } + return result; +} +int CategorizedView::itemHeightForCategoryRow(const CategorizedViewCategory *category, const int internalRow) const +{ + foreach (const QModelIndex &i, category->items()) { QPair pos = categoryInternalPosition(i); if (pos.second == internalRow) { - return itemSize(i).height(); + return categoryRowHeight(i); } } return -1; @@ -586,13 +440,10 @@ void CategorizedView::paintEvent(QPaintEvent *event) QPainter painter(this->viewport()); painter.translate(-offset()); - // FIXME we shouldn't need to do this - invalidateCaches(); - int y = 0; for (int i = 0; i < m_categories.size(); ++i) { - Category *category = m_categories.at(i); + CategorizedViewCategory *category = m_categories.at(i); category->drawHeader(&painter, y); y += category->totalHeight() + m_categoryMargin; } @@ -627,16 +478,16 @@ void CategorizedView::paintEvent(QPaintEvent *event) if (!m_lastDragPosition.isNull()) { - QPair pair = rowDropPos(m_lastDragPosition); - Category *category = pair.first; + QPair pair = rowDropPos(m_lastDragPosition); + CategorizedViewCategory *category = pair.first; int row = pair.second; if (category) { - int internalRow = row - firstItemForCategory(category).row(); + int internalRow = row - category->firstRow; QLine line; - if (internalRow >= numItemsForCategory(category)) + if (internalRow >= category->numItems()) { - QRect toTheRightOfRect = visualRect(lastItemForCategory(category)); + QRect toTheRightOfRect = visualRect(category->lastItem()); line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight()); } else @@ -700,8 +551,8 @@ void CategorizedView::dropEvent(QDropEvent *event) return; } - QPair dropPos = rowDropPos(event->pos() + offset()); - const Category *category = dropPos.first; + QPair dropPos = rowDropPos(event->pos() + offset()); + const CategorizedViewCategory *category = dropPos.first; const int row = dropPos.second; if (row == -1) @@ -772,24 +623,17 @@ QRect CategorizedView::visualRect(const QModelIndex &index) const return QRect(); } - if (!m_cachedVisualRects.contains(index)) - { - const Category *cat = category(index); - QPair pos = categoryInternalPosition(index); - int x = pos.first; - int y = pos.second; + const CategorizedViewCategory *cat = category(index); + QPair pos = categoryInternalPosition(index); + int x = pos.first; + int y = pos.second; - QSize size = itemSize(index); + QRect out; + out.setTop(cat->top() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); + out.setLeft(spacing() + x * itemWidth() + x * spacing()); + out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); - QRect *out = new QRect; - out->setTop(categoryTop(cat) + cat->headerHeight() + 5 + y * size.height()); - out->setLeft(spacing() + x * itemWidth() + x * spacing()); - out->setSize(size); - - m_cachedVisualRects.insert(index, out); - } - - return *m_cachedVisualRects.object(index); + return out; } /* void CategorizedView::startCategoryEditor(Category *category) @@ -908,13 +752,13 @@ bool CategorizedView::isDragEventAccepted(QDropEvent *event) } return true; } -QPair CategorizedView::rowDropPos(const QPoint &pos) +QPair CategorizedView::rowDropPos(const QPoint &pos) { // check that we aren't on a category header and calculate which category we're in - Category *category = 0; + CategorizedViewCategory *category = 0; { int y = 0; - foreach (Category *cat, m_categories) + foreach (CategorizedViewCategory *cat, m_categories) { if (pos.y() > y && pos.y() < (y + cat->headerHeight())) { @@ -933,7 +777,7 @@ QPair CategorizedView::rowDropPos(const QPoint } } - QList indices = itemsForCategory(category); + QList indices = category->items(); // calculate the internal column int internalColumn = -1; @@ -967,7 +811,7 @@ QPair CategorizedView::rowDropPos(const QPoint int internalRow = -1; { // FIXME rework the drag and drop code - const int top = categoryTop(category); + const int top = category->top(); for (int r = 0, h = top; r < category->numRows(); h += itemHeightForCategoryRow(category, r), ++r) { if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r))) @@ -1000,14 +844,6 @@ QPair CategorizedView::rowDropPos(const QPoint return qMakePair(category, indices.at(categoryRow).row()); } -void CategorizedView::invalidateCaches() -{ - m_cachedItemWidth = -1; - m_cachedCategoryToIndexMapping.clear(); - m_cachedVisualRects.clear(); - m_cachedItemSizes.clear(); -} - QPoint CategorizedView::offset() const { return QPoint(horizontalOffset(), verticalOffset()); diff --git a/CategorizedView.h b/CategorizedView.h index 08d43be8..0550c7f8 100644 --- a/CategorizedView.h +++ b/CategorizedView.h @@ -3,7 +3,6 @@ #include #include -#include struct CategorizedViewRoles { @@ -15,6 +14,8 @@ struct CategorizedViewRoles }; }; +struct CategorizedViewCategory; + class CategorizedView : public QListView { Q_OBJECT @@ -28,7 +29,7 @@ public: void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; protected slots: - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); virtual void rowsInserted(const QModelIndex &parent, int start, int end); virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); virtual void updateGeometries(); @@ -50,57 +51,27 @@ protected: void startDrag(Qt::DropActions supportedActions) override; private: - struct Category - { - Category(const QString &text, CategorizedView *view); - Category(const Category *other); - CategorizedView *view; - QString text; - bool collapsed; - QRect iconRect; - QRect textRect; + friend struct CategorizedViewCategory; - void drawHeader(QPainter *painter, const int y); - int totalHeight() const; - int headerHeight() const; - int contentHeight() const; - int numRows() const; - }; - friend struct Category; - - QList m_categories; - mutable QCache > m_cachedCategoryToIndexMapping; - mutable QCache m_cachedVisualRects; + QList m_categories; int m_leftMargin; int m_rightMargin; int m_bottomMargin; int m_categoryMargin; - int m_itemSpacing; //bool m_updatesDisabled; - Category *category(const QModelIndex &index) const; - Category *category(const QString &cat) const; - Category *categoryAt(const QPoint &pos) const; - int numItemsForCategory(const Category *category) const; - QList itemsForCategory(const Category *category) const; - QModelIndex firstItemForCategory(const Category *category) const; - QModelIndex lastItemForCategory(const Category *category) const; - - int categoryTop(const Category *category) const; + CategorizedViewCategory *category(const QModelIndex &index) const; + CategorizedViewCategory *category(const QString &cat) const; + CategorizedViewCategory *categoryAt(const QPoint &pos) const; int itemsPerRow() const; int contentWidth() const; - static bool lessThanCategoryPointer(const Category *c1, const Category *c2); - QList sortedCategories() const; - private: - mutable int m_cachedItemWidth; - mutable QCache m_cachedItemSizes; int itemWidth() const; - QSize itemSize(const QModelIndex &index) const; + int categoryRowHeight(const QModelIndex &index) const; /*QLineEdit *m_categoryEditor; Category *m_editedCategory; @@ -113,21 +84,20 @@ private: QPoint m_pressedPosition; QPersistentModelIndex m_pressedIndex; bool m_pressedAlreadySelected; - Category *m_pressedCategory; + CategorizedViewCategory *m_pressedCategory; QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; QPoint m_lastDragPosition; QPair categoryInternalPosition(const QModelIndex &index) const; - int itemHeightForCategoryRow(const Category *category, const int internalRow) const; + int categoryInternalRowTop(const QModelIndex &index) const; + int itemHeightForCategoryRow(const CategorizedViewCategory *category, const int internalRow) const; QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; QList > draggablePaintPairs(const QModelIndexList &indices, QRect *r) const; bool isDragEventAccepted(QDropEvent *event); - QPair rowDropPos(const QPoint &pos); - - void invalidateCaches(); + QPair rowDropPos(const QPoint &pos); QPoint offset() const; }; diff --git a/CategorizedViewCategory.cpp b/CategorizedViewCategory.cpp new file mode 100644 index 00000000..b82ffc96 --- /dev/null +++ b/CategorizedViewCategory.cpp @@ -0,0 +1,128 @@ +#include "CategorizedViewCategory.h" + +#include +#include +#include + +#include "CategorizedView.h" + +CategorizedViewCategory::CategorizedViewCategory(const QString &text, CategorizedView *view) + : view(view), text(text), collapsed(false) +{ +} +CategorizedViewCategory::CategorizedViewCategory(const CategorizedViewCategory *other) : + view(other->view), text(other->text), collapsed(other->collapsed), iconRect(other->iconRect), textRect(other->textRect) +{ +} + +void CategorizedViewCategory::update() +{ + firstRow = firstItem().row(); + + rowHeights = QVector(numRows()); + for (int i = 0; i < numRows(); ++i) + { + rowHeights[i] = view->categoryRowHeight(view->model()->index(i * view->itemsPerRow() + firstRow, 0)); + } +} + +void CategorizedViewCategory::drawHeader(QPainter *painter, const int y) +{ + painter->save(); + + int height = headerHeight() - 4; + int collapseSize = height; + + // the icon + iconRect = QRect(view->m_rightMargin + 2, 2 + y, collapseSize, collapseSize); + painter->setPen(QPen(Qt::black, 1)); + painter->drawRect(iconRect); + static const int margin = 2; + QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); + int midX = iconSubrect.center().x(); + int midY = iconSubrect.center().y(); + if (collapsed) + { + painter->drawLine(midX, iconSubrect.top(), midX, iconSubrect.bottom()); + } + painter->drawLine(iconSubrect.left(), midY, iconSubrect.right(), midY); + + // the text + int textWidth = painter->fontMetrics().width(text); + textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); + painter->setBrush(view->viewOptions().palette.text()); + view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, view->viewport()->palette(), true, text); + + // the line + painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); + + painter->restore(); +} + +int CategorizedViewCategory::totalHeight() const +{ + return headerHeight() + 5 + contentHeight(); +} +int CategorizedViewCategory::headerHeight() const +{ + return view->viewport()->fontMetrics().height() + 4; +} +int CategorizedViewCategory::contentHeight() const +{ + if (collapsed) + { + return 0; + } + int result = 0; + for (int i = 0; i < rowHeights.size(); ++i) + { + result += rowHeights[i]; + } + return result; +} +int CategorizedViewCategory::numRows() const +{ + return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow())); +} +int CategorizedViewCategory::top() const +{ + int res = 0; + const QList cats = view->m_categories; + for (int i = 0; i < cats.size(); ++i) + { + if (cats.at(i) == this) + { + break; + } + res += cats.at(i)->totalHeight() + view->m_categoryMargin; + } + return res; +} + +QList CategorizedViewCategory::items() const +{ + QList indices; + for (int i = 0; i < view->model()->rowCount(); ++i) + { + const QModelIndex index = view->model()->index(i, 0); + if (index.data(CategorizedViewRoles::CategoryRole).toString() == text) + { + indices.append(index); + } + } + return indices; +} +int CategorizedViewCategory::numItems() const +{ + return items().size(); +} +QModelIndex CategorizedViewCategory::firstItem() const +{ + QList indices = items(); + return indices.isEmpty() ? QModelIndex() : indices.first(); +} +QModelIndex CategorizedViewCategory::lastItem() const +{ + QList indices = items(); + return indices.isEmpty() ? QModelIndex() : indices.last(); +} diff --git a/CategorizedViewCategory.h b/CategorizedViewCategory.h new file mode 100644 index 00000000..cb6ef8c5 --- /dev/null +++ b/CategorizedViewCategory.h @@ -0,0 +1,39 @@ +#ifndef CATEGORIZEDVIEWROW_H +#define CATEGORIZEDVIEWROW_H + +#include +#include +#include + +class CategorizedView; +class QPainter; +class QModelIndex; + +struct CategorizedViewCategory +{ + CategorizedViewCategory(const QString &text, CategorizedView *view); + CategorizedViewCategory(const CategorizedViewCategory *other); + CategorizedView *view; + QString text; + bool collapsed; + QRect iconRect; + QRect textRect; + QVector rowHeights; + int firstRow; + + void update(); + + void drawHeader(QPainter *painter, const int y); + int totalHeight() const; + int headerHeight() const; + int contentHeight() const; + int numRows() const; + int top() const; + + QList items() const; + int numItems() const; + QModelIndex firstItem() const; + QModelIndex lastItem() const; +}; + +#endif // CATEGORIZEDVIEWROW_H From a1a06cc89f7f1d904a1b71d330d6129b866ff29b Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 07:33:32 +0100 Subject: [PATCH 13/49] Derpstances. Everything renamed. Launching does not yet work. --- CMakeLists.txt | 36 +-- gui/MainWindow.cpp | 4 +- ...odEditDialog.cpp => DerpModEditDialog.cpp} | 114 ++----- ...SixModEditDialog.h => DerpModEditDialog.h} | 18 +- ...xModEditDialog.ui => DerpModEditDialog.ui} | 27 +- logic/BaseInstance.h | 2 +- ...SixFTBInstance.cpp => DerpFTBInstance.cpp} | 40 ++- ...{OneSixFTBInstance.h => DerpFTBInstance.h} | 10 +- .../{OneSixInstance.cpp => DerpInstance.cpp} | 161 ++++------ logic/{OneSixInstance.h => DerpInstance.h} | 29 +- .../{OneSixInstance_p.h => DerpInstance_p.h} | 15 +- logic/{OneSixLibrary.cpp => DerpLibrary.cpp} | 38 +-- logic/{OneSixLibrary.h => DerpLibrary.h} | 32 +- logic/{OneSixRule.cpp => DerpRule.cpp} | 4 +- logic/{OneSixRule.h => DerpRule.h} | 10 +- logic/{OneSixUpdate.cpp => DerpUpdate.cpp} | 58 ++-- logic/{OneSixUpdate.h => DerpUpdate.h} | 4 +- logic/DerpVersion.cpp | 164 ++++++++++ logic/DerpVersion.h | 110 +++++++ logic/DerpVersionBuilder.cpp | 279 ++++++++++++++++++ logic/DerpVersionBuilder.h | 43 +++ logic/ForgeInstaller.cpp | 22 +- logic/ForgeInstaller.h | 6 +- logic/InstanceFactory.cpp | 36 +-- logic/LegacyInstance.h | 2 +- logic/LiteLoaderInstaller.cpp | 25 +- logic/LiteLoaderInstaller.h | 8 +- logic/MinecraftVersion.h | 6 +- logic/NostalgiaInstance.cpp | 2 +- logic/NostalgiaInstance.h | 4 +- logic/OneSixVersion.cpp | 50 ++-- logic/OneSixVersion.h | 14 +- logic/lists/MinecraftVersionList.cpp | 6 +- 33 files changed, 920 insertions(+), 459 deletions(-) rename gui/dialogs/{OneSixModEditDialog.cpp => DerpModEditDialog.cpp} (69%) rename gui/dialogs/{OneSixModEditDialog.h => DerpModEditDialog.h} (80%) rename gui/dialogs/{OneSixModEditDialog.ui => DerpModEditDialog.ui} (91%) rename logic/{OneSixFTBInstance.cpp => DerpFTBInstance.cpp} (67%) rename logic/{OneSixFTBInstance.h => DerpFTBInstance.h} (56%) rename logic/{OneSixInstance.cpp => DerpInstance.cpp} (70%) rename logic/{OneSixInstance.h => DerpInstance.h} (76%) rename logic/{OneSixInstance_p.h => DerpInstance_p.h} (74%) rename logic/{OneSixLibrary.cpp => DerpLibrary.cpp} (87%) rename logic/{OneSixLibrary.h => DerpLibrary.h} (85%) rename logic/{OneSixRule.cpp => DerpRule.cpp} (98%) rename logic/{OneSixRule.h => DerpRule.h} (90%) rename logic/{OneSixUpdate.cpp => DerpUpdate.cpp} (88%) rename logic/{OneSixUpdate.h => DerpUpdate.h} (92%) create mode 100644 logic/DerpVersion.cpp create mode 100644 logic/DerpVersion.h create mode 100644 logic/DerpVersionBuilder.cpp create mode 100644 logic/DerpVersionBuilder.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b276cf8..43de626b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,8 +297,8 @@ gui/dialogs/IconPickerDialog.h gui/dialogs/IconPickerDialog.cpp gui/dialogs/LegacyModEditDialog.h gui/dialogs/LegacyModEditDialog.cpp -gui/dialogs/OneSixModEditDialog.h -gui/dialogs/OneSixModEditDialog.cpp +gui/dialogs/DerpModEditDialog.h +gui/dialogs/DerpModEditDialog.cpp gui/dialogs/ModEditDialogCommon.h gui/dialogs/ModEditDialogCommon.cpp gui/dialogs/EditNotesDialog.h @@ -405,32 +405,34 @@ logic/LegacyUpdate.cpp logic/LegacyForge.h logic/LegacyForge.cpp -# 1.6 instances -logic/OneSixInstance.h -logic/OneSixInstance.cpp -logic/OneSixInstance_p.h -logic/OneSixUpdate.h -logic/OneSixUpdate.cpp -logic/OneSixVersion.h -logic/OneSixVersion.cpp -logic/OneSixLibrary.h -logic/OneSixLibrary.cpp -logic/OneSixRule.h -logic/OneSixRule.cpp +# Derp instances +logic/DerpUpdate.h +logic/DerpUpdate.cpp +logic/DerpVersion.h +logic/DerpVersion.cpp +logic/DerpLibrary.h +logic/DerpLibrary.cpp +logic/DerpRule.h +logic/DerpRule.cpp logic/OpSys.h logic/OpSys.cpp logic/ForgeInstaller.h logic/ForgeInstaller.cpp logic/LiteLoaderInstaller.h logic/LiteLoaderInstaller.cpp +logic/DerpInstance.h +logic/DerpInstance.cpp +logic/DerpInstance_p.h +logic/DerpVersionBuilder.h +logic/DerpVersionBuilder.cpp # Nostalgia logic/NostalgiaInstance.h logic/NostalgiaInstance.cpp # FTB -logic/OneSixFTBInstance.h -logic/OneSixFTBInstance.cpp +logic/DerpFTBInstance.h +logic/DerpFTBInstance.cpp logic/LegacyFTBInstance.h logic/LegacyFTBInstance.cpp @@ -506,7 +508,7 @@ gui/dialogs/InstanceSettings.ui gui/dialogs/ProgressDialog.ui gui/dialogs/IconPickerDialog.ui gui/dialogs/LegacyModEditDialog.ui -gui/dialogs/OneSixModEditDialog.ui +gui/dialogs/DerpModEditDialog.ui gui/dialogs/EditNotesDialog.ui gui/dialogs/AccountListDialog.ui gui/dialogs/AccountSelectDialog.ui diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index ee9c3fad..ba394a94 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -84,7 +84,7 @@ #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logic/MinecraftProcess.h" -#include "logic/OneSixUpdate.h" +#include "logic/DerpUpdate.h" #include "logic/JavaUtils.h" #include "logic/NagUtils.h" #include "logic/SkinUtils.h" @@ -1257,7 +1257,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() VersionSelectDialog vselect(m_selectedInstance->versionList().get(), tr("Change Minecraft version"), this); - vselect.setFilter(1, "OneSix"); + vselect.setFilter(1, "Derp"); if(!vselect.exec() || !vselect.selectedVersion()) return; diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/DerpModEditDialog.cpp similarity index 69% rename from gui/dialogs/OneSixModEditDialog.cpp rename to gui/dialogs/DerpModEditDialog.cpp index 3982f17d..be17404c 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/DerpModEditDialog.cpp @@ -23,9 +23,9 @@ #include #include -#include "OneSixModEditDialog.h" +#include "DerpModEditDialog.h" #include "ModEditDialogCommon.h" -#include "ui_OneSixModEditDialog.h" +#include "ui_DerpModEditDialog.h" #include "gui/Platform.h" #include "gui/dialogs/CustomMessageBox.h" @@ -34,14 +34,14 @@ #include "gui/dialogs/ProgressDialog.h" #include "logic/ModList.h" -#include "logic/OneSixVersion.h" +#include "logic/DerpVersion.h" #include "logic/EnabledItemFilter.h" #include "logic/lists/ForgeVersionList.h" #include "logic/ForgeInstaller.h" #include "logic/LiteLoaderInstaller.h" -OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) - : QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst) +DerpModEditDialog::DerpModEditDialog(DerpInstance *inst, QWidget *parent) + : QDialog(parent), ui(new Ui::DerpModEditDialog), m_inst(inst) { MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); @@ -81,64 +81,35 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) ui->resPackTreeView->installEventFilter(this); m_resourcepacks->startWatching(); } + + connect(m_inst, &DerpInstance::versionReloaded, this, &DerpModEditDialog::updateVersionControls); } -OneSixModEditDialog::~OneSixModEditDialog() +DerpModEditDialog::~DerpModEditDialog() { m_mods->stopWatching(); m_resourcepacks->stopWatching(); delete ui; } -void OneSixModEditDialog::updateVersionControls() +void DerpModEditDialog::updateVersionControls() { bool customVersion = m_inst->versionIsCustom(); - ui->customizeBtn->setEnabled(!customVersion); - ui->revertBtn->setEnabled(customVersion); ui->forgeBtn->setEnabled(true); ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply()); ui->customEditorBtn->setEnabled(customVersion); } -void OneSixModEditDialog::disableVersionControls() +void DerpModEditDialog::disableVersionControls() { - ui->customizeBtn->setEnabled(false); - ui->revertBtn->setEnabled(false); ui->forgeBtn->setEnabled(false); ui->liteloaderBtn->setEnabled(false); ui->customEditorBtn->setEnabled(false); } -void OneSixModEditDialog::on_customizeBtn_clicked() +void DerpModEditDialog::on_customEditorBtn_clicked() { - if (m_inst->customizeVersion()) - { - m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.get()); - updateVersionControls(); - } -} - -void OneSixModEditDialog::on_revertBtn_clicked() -{ - auto response = CustomMessageBox::selectable( - this, tr("Revert?"), tr("Do you want to revert the " - "version of this instance to its original configuration?"), - QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec(); - if (response == QMessageBox::Yes) - { - if (m_inst->revertCustomVersion()) - { - m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.get()); - updateVersionControls(); - } - } -} - -void OneSixModEditDialog::on_customEditorBtn_clicked() -{ - if (m_inst->versionIsCustom()) + if (QDir(m_inst->instanceRoot()).exists("custom.json")) { if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json")) { @@ -147,40 +118,12 @@ void OneSixModEditDialog::on_customEditorBtn_clicked() } } -void OneSixModEditDialog::on_forgeBtn_clicked() +void DerpModEditDialog::on_forgeBtn_clicked() { VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); vselect.setFilter(1, m_inst->currentVersionId()); if (vselect.exec() && vselect.selectedVersion()) { - if (m_inst->versionIsCustom()) - { - auto reply = QMessageBox::question( - this, tr("Revert?"), - tr("This will revert any " - "changes you did to the version up to this point. Is that " - "OK?"), - QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::Yes) - { - m_inst->revertCustomVersion(); - m_inst->customizeVersion(); - { - m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.get()); - updateVersionControls(); - } - } - else - return; - } - else - { - m_inst->customizeVersion(); - m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.get()); - updateVersionControls(); - } ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast(vselect.selectedVersion()); if (!forgeVersion) @@ -220,7 +163,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked() } } -void OneSixModEditDialog::on_liteloaderBtn_clicked() +void DerpModEditDialog::on_liteloaderBtn_clicked() { LiteLoaderInstaller liteloader(m_inst->intendedVersionId()); if (!liteloader.canApply()) @@ -231,13 +174,6 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked() "into this version of Minecraft")); return; } - if (!m_inst->versionIsCustom()) - { - m_inst->customizeVersion(); - m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.get()); - updateVersionControls(); - } if (!liteloader.apply(m_version)) { QMessageBox::critical( @@ -246,7 +182,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked() } } -bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent) +bool DerpModEditDialog::loaderListFilter(QKeyEvent *keyEvent) { switch (keyEvent->key()) { @@ -262,7 +198,7 @@ bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent) return QDialog::eventFilter(ui->loaderModTreeView, keyEvent); } -bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) +bool DerpModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) { switch (keyEvent->key()) { @@ -278,7 +214,7 @@ bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) return QDialog::eventFilter(ui->resPackTreeView, keyEvent); } -bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev) +bool DerpModEditDialog::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() != QEvent::KeyPress) { @@ -292,12 +228,12 @@ bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev) return QDialog::eventFilter(obj, ev); } -void OneSixModEditDialog::on_buttonBox_rejected() +void DerpModEditDialog::on_buttonBox_rejected() { close(); } -void OneSixModEditDialog::on_addModBtn_clicked() +void DerpModEditDialog::on_addModBtn_clicked() { QStringList fileNames = QFileDialog::getOpenFileNames( this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods")); @@ -308,7 +244,7 @@ void OneSixModEditDialog::on_addModBtn_clicked() m_mods->startWatching(); } } -void OneSixModEditDialog::on_rmModBtn_clicked() +void DerpModEditDialog::on_rmModBtn_clicked() { int first, last; auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); @@ -319,12 +255,12 @@ void OneSixModEditDialog::on_rmModBtn_clicked() m_mods->deleteMods(first, last); m_mods->startWatching(); } -void OneSixModEditDialog::on_viewModBtn_clicked() +void DerpModEditDialog::on_viewModBtn_clicked() { openDirInDefaultProgram(m_inst->loaderModsDir(), true); } -void OneSixModEditDialog::on_addResPackBtn_clicked() +void DerpModEditDialog::on_addResPackBtn_clicked() { QStringList fileNames = QFileDialog::getOpenFileNames( this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs")); @@ -335,7 +271,7 @@ void OneSixModEditDialog::on_addResPackBtn_clicked() m_resourcepacks->startWatching(); } } -void OneSixModEditDialog::on_rmResPackBtn_clicked() +void DerpModEditDialog::on_rmResPackBtn_clicked() { int first, last; auto list = ui->resPackTreeView->selectionModel()->selectedRows(); @@ -346,12 +282,12 @@ void OneSixModEditDialog::on_rmResPackBtn_clicked() m_resourcepacks->deleteMods(first, last); m_resourcepacks->startWatching(); } -void OneSixModEditDialog::on_viewResPackBtn_clicked() +void DerpModEditDialog::on_viewResPackBtn_clicked() { openDirInDefaultProgram(m_inst->resourcePacksDir(), true); } -void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous) +void DerpModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous) { if (!current.isValid()) { diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/DerpModEditDialog.h similarity index 80% rename from gui/dialogs/OneSixModEditDialog.h rename to gui/dialogs/DerpModEditDialog.h index 2510c59c..e9e61c65 100644 --- a/gui/dialogs/OneSixModEditDialog.h +++ b/gui/dialogs/DerpModEditDialog.h @@ -16,21 +16,21 @@ #pragma once #include -#include +#include class EnabledItemFilter; namespace Ui { -class OneSixModEditDialog; +class DerpModEditDialog; } -class OneSixModEditDialog : public QDialog +class DerpModEditDialog : public QDialog { Q_OBJECT public: - explicit OneSixModEditDialog(OneSixInstance *inst, QWidget *parent = 0); - virtual ~OneSixModEditDialog(); + explicit DerpModEditDialog(DerpInstance *inst, QWidget *parent = 0); + virtual ~DerpModEditDialog(); private slots: @@ -45,8 +45,6 @@ slots: void on_buttonBox_rejected(); void on_forgeBtn_clicked(); void on_liteloaderBtn_clicked(); - void on_customizeBtn_clicked(); - void on_revertBtn_clicked(); void on_customEditorBtn_clicked(); void updateVersionControls(); void disableVersionControls(); @@ -57,12 +55,12 @@ protected: bool resourcePackListFilter(QKeyEvent *ev); private: - Ui::OneSixModEditDialog *ui; - std::shared_ptr m_version; + Ui::DerpModEditDialog *ui; + std::shared_ptr m_version; std::shared_ptr m_mods; std::shared_ptr m_resourcepacks; EnabledItemFilter *main_model; - OneSixInstance *m_inst; + DerpInstance *m_inst; public slots: void loaderCurrent(QModelIndex current, QModelIndex previous); diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/DerpModEditDialog.ui similarity index 91% rename from gui/dialogs/OneSixModEditDialog.ui rename to gui/dialogs/DerpModEditDialog.ui index 899e0cbf..7aaf1564 100644 --- a/gui/dialogs/OneSixModEditDialog.ui +++ b/gui/dialogs/DerpModEditDialog.ui @@ -1,7 +1,7 @@ - OneSixModEditDialog - + DerpModEditDialog + 0 @@ -84,29 +84,6 @@ - - - - Create an customized copy of the base version - - - Customize - - - - - - - false - - - Revert to original base version - - - Revert - - - diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index a861e9b2..79640c84 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -27,7 +27,7 @@ class QDialog; class Task; class MinecraftProcess; -class OneSixUpdate; +class DerpUpdate; class InstanceList; class BaseInstancePrivate; diff --git a/logic/OneSixFTBInstance.cpp b/logic/DerpFTBInstance.cpp similarity index 67% rename from logic/OneSixFTBInstance.cpp rename to logic/DerpFTBInstance.cpp index e50a5b53..f9aeeca0 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/DerpFTBInstance.cpp @@ -1,17 +1,17 @@ -#include "OneSixFTBInstance.h" +#include "DerpFTBInstance.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" #include "tasks/SequentialTask.h" #include "ForgeInstaller.h" #include "lists/ForgeVersionList.h" #include "MultiMC.h" -class OneSixFTBInstanceForge : public Task +class DerpFTBInstanceForge : public Task { Q_OBJECT public: - explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) : + explicit DerpFTBInstanceForge(const QString &version, DerpFTBInstance *inst, QObject *parent = 0) : Task(parent), instance(inst), version("Forge " + version) { } @@ -38,7 +38,7 @@ public: fjob = new NetJob("Forge download"); fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry)); connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);}); - connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge); + connect(fjob, &NetJob::succeeded, this, &DerpFTBInstanceForge::installForge); connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); }); fjob->start(); } @@ -60,8 +60,6 @@ slots: emitFailed(tr("Couldn't load the version config")); return; } - instance->revertCustomVersion(); - instance->customizeVersion(); auto version = instance->getFullVersion(); if (!forge.apply(version)) { @@ -72,41 +70,41 @@ slots: } private: - OneSixFTBInstance *instance; + DerpFTBInstance *instance; QString version; ForgeVersionPtr forgeVersion; MetaEntryPtr entry; NetJob *fjob; }; -OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : - OneSixInstance(rootDir, settings, parent) +DerpFTBInstance::DerpFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + DerpInstance(rootDir, settings, parent) { QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json")); if (f.open(QFile::ReadOnly)) { QString data = QString::fromUtf8(f.readAll()); QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data); - m_forge.reset(new OneSixLibrary(match.captured())); + m_forge.reset(new DerpLibrary(match.captured())); m_forge->finalize(); } } -QString OneSixFTBInstance::id() const +QString DerpFTBInstance::id() const { return "FTB/" + BaseInstance::id(); } -QString OneSixFTBInstance::getStatusbarDescription() +QString DerpFTBInstance::getStatusbarDescription() { - return "OneSix FTB: " + intendedVersionId(); + return "Derp FTB: " + intendedVersionId(); } -bool OneSixFTBInstance::menuActionEnabled(QString action_name) const +bool DerpFTBInstance::menuActionEnabled(QString action_name) const { return false; } -std::shared_ptr OneSixFTBInstance::doUpdate(bool only_prepare) +std::shared_ptr DerpFTBInstance::doUpdate(bool only_prepare) { std::shared_ptr task; task.reset(new SequentialTask(this)); @@ -114,12 +112,12 @@ std::shared_ptr OneSixFTBInstance::doUpdate(bool only_prepare) { task->addTask(std::shared_ptr(MMC->forgelist()->getLoadTask())); } - task->addTask(OneSixInstance::doUpdate(only_prepare)); - task->addTask(std::shared_ptr(new OneSixFTBInstanceForge(m_forge->version(), this, this))); + task->addTask(DerpInstance::doUpdate(only_prepare)); + task->addTask(std::shared_ptr(new DerpFTBInstanceForge(m_forge->version(), this, this))); //FIXME: yes. this may appear dumb. but the previous step can change the list, so we do it all again. //TODO: Add a graph task. Construct graphs of tasks so we may capture the logic properly. - task->addTask(OneSixInstance::doUpdate(only_prepare)); + task->addTask(DerpInstance::doUpdate(only_prepare)); return task; } -#include "OneSixFTBInstance.moc" +#include "DerpFTBInstance.moc" diff --git a/logic/OneSixFTBInstance.h b/logic/DerpFTBInstance.h similarity index 56% rename from logic/OneSixFTBInstance.h rename to logic/DerpFTBInstance.h index dc028819..c16998bf 100644 --- a/logic/OneSixFTBInstance.h +++ b/logic/DerpFTBInstance.h @@ -1,14 +1,14 @@ #pragma once -#include "OneSixInstance.h" +#include "DerpInstance.h" -class OneSixLibrary; +class DerpLibrary; -class OneSixFTBInstance : public OneSixInstance +class DerpFTBInstance : public DerpInstance { Q_OBJECT public: - explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, + explicit DerpFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent = 0); virtual QString getStatusbarDescription(); virtual bool menuActionEnabled(QString action_name) const; @@ -18,5 +18,5 @@ public: virtual QString id() const; private: - std::shared_ptr m_forge; + std::shared_ptr m_forge; }; diff --git a/logic/OneSixInstance.cpp b/logic/DerpInstance.cpp similarity index 70% rename from logic/OneSixInstance.cpp rename to logic/DerpInstance.cpp index 3cfc1c76..31ed7c95 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/DerpInstance.cpp @@ -13,37 +13,35 @@ * limitations under the License. */ -#include "MultiMC.h" -#include "OneSixInstance.h" -#include "OneSixInstance_p.h" -#include "OneSixUpdate.h" -#include "MinecraftProcess.h" -#include "OneSixVersion.h" -#include "JavaChecker.h" -#include "logic/icons/IconList.h" +#include "DerpInstance.h" -#include -#include -#include -#include -#include "gui/dialogs/OneSixModEditDialog.h" -#include "logger/QsLog.h" -#include "logic/assets/AssetsUtils.h" #include -OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, - QObject *parent) - : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent) +#include "DerpInstance_p.h" +#include "DerpUpdate.h" +#include "DerpVersion.h" +#include "pathutils.h" +#include "logger/QsLog.h" +#include "assets/AssetsUtils.h" +#include "MultiMC.h" +#include "icons/IconList.h" +#include "MinecraftProcess.h" +#include "gui/dialogs/DerpModEditDialog.h" + +DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) + : BaseInstance(new DerpInstancePrivate(), rootDir, settings, parent) { - I_D(OneSixInstance); + I_D(DerpInstance); d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); + d->version.reset(new DerpVersion(this, this)); reloadFullVersion(); } -std::shared_ptr OneSixInstance::doUpdate(bool only_prepare) + +std::shared_ptr DerpInstance::doUpdate(bool only_prepare) { - return std::shared_ptr(new OneSixUpdate(this, only_prepare)); + return std::shared_ptr(new DerpUpdate(this, only_prepare)); } QString replaceTokensIn(QString text, QMap with) @@ -70,7 +68,7 @@ QString replaceTokensIn(QString text, QMap with) return result; } -QDir OneSixInstance::reconstructAssets(std::shared_ptr version) +QDir DerpInstance::reconstructAssets(std::shared_ptr version) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); @@ -130,9 +128,9 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr version) return virtualRoot; } -QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) +QStringList DerpInstance::processMinecraftArgs(MojangAccountPtr account) { - I_D(OneSixInstance); + I_D(DerpInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; @@ -182,9 +180,9 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) return parts; } -MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) +MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) { - I_D(OneSixInstance); + I_D(DerpInstance); QIcon icon = MMC->icons()->getIcon(iconKey()); auto pixmap = icon.pixmap(128, 128); @@ -229,7 +227,9 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) QDir natives_dir(PathCombine(instanceRoot(), "natives/")); launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "natives " + natives_dir.absolutePath() + "\n"; - launchScript += "launch onesix\n"; + launchScript += "launch onesix"; + + qDebug() << launchScript; // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); @@ -239,16 +239,16 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) return proc; } -void OneSixInstance::cleanupAfterRun() +void DerpInstance::cleanupAfterRun() { QString target_dir = PathCombine(instanceRoot(), "natives/"); QDir dir(target_dir); dir.removeRecursively(); } -std::shared_ptr OneSixInstance::loaderModList() +std::shared_ptr DerpInstance::loaderModList() { - I_D(OneSixInstance); + I_D(DerpInstance); if (!d->loader_mod_list) { d->loader_mod_list.reset(new ModList(loaderModsDir())); @@ -257,9 +257,9 @@ std::shared_ptr OneSixInstance::loaderModList() return d->loader_mod_list; } -std::shared_ptr OneSixInstance::resourcePackList() +std::shared_ptr DerpInstance::resourcePackList() { - I_D(OneSixInstance); + I_D(DerpInstance); if (!d->resource_pack_list) { d->resource_pack_list.reset(new ModList(resourcePacksDir())); @@ -268,34 +268,32 @@ std::shared_ptr OneSixInstance::resourcePackList() return d->resource_pack_list; } -QDialog *OneSixInstance::createModEditDialog(QWidget *parent) +QDialog *DerpInstance::createModEditDialog(QWidget *parent) { - return new OneSixModEditDialog(this, parent); + return new DerpModEditDialog(this, parent); } -bool OneSixInstance::setIntendedVersionId(QString version) +bool DerpInstance::setIntendedVersionId(QString version) { settings().set("IntendedVersion", version); setShouldUpdate(true); - auto pathCustom = PathCombine(instanceRoot(), "custom.json"); auto pathOrig = PathCombine(instanceRoot(), "version.json"); - QFile::remove(pathCustom); QFile::remove(pathOrig); reloadFullVersion(); return true; } -QString OneSixInstance::intendedVersionId() const +QString DerpInstance::intendedVersionId() const { return settings().get("IntendedVersion").toString(); } -void OneSixInstance::setShouldUpdate(bool val) +void DerpInstance::setShouldUpdate(bool val) { settings().set("ShouldUpdate", val); } -bool OneSixInstance::shouldUpdate() const +bool DerpInstance::shouldUpdate() const { QVariant var = settings().get("ShouldUpdate"); if (!var.isValid() || var.toBool() == false) @@ -305,94 +303,53 @@ bool OneSixInstance::shouldUpdate() const return true; } -bool OneSixInstance::versionIsCustom() +bool DerpInstance::versionIsCustom() { - QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); - QFile versionfile(verpath_custom); - return versionfile.exists(); + QDir patches(PathCombine(instanceRoot(), "patches/")); + return QFile::exists(PathCombine(instanceRoot(), "custom.json")) + || (patches.exists() && patches.count() >= 0); } -QString OneSixInstance::currentVersionId() const +QString DerpInstance::currentVersionId() const { return intendedVersionId(); } -bool OneSixInstance::customizeVersion() +bool DerpInstance::reloadFullVersion(QWidget *widgetParent) { - if (!versionIsCustom()) - { - auto pathCustom = PathCombine(instanceRoot(), "custom.json"); - auto pathOrig = PathCombine(instanceRoot(), "version.json"); - QFile::copy(pathOrig, pathCustom); - return reloadFullVersion(); - } - else - return true; + I_D(DerpInstance); + + bool ret = d->version->reload(widgetParent); + emit versionReloaded(); + return ret; } -bool OneSixInstance::revertCustomVersion() +std::shared_ptr DerpInstance::getFullVersion() { - if (versionIsCustom()) - { - auto path = PathCombine(instanceRoot(), "custom.json"); - QFile::remove(path); - return reloadFullVersion(); - } - else - return true; -} - -bool OneSixInstance::reloadFullVersion() -{ - I_D(OneSixInstance); - - QString verpath = PathCombine(instanceRoot(), "version.json"); - { - QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); - QFile versionfile(verpath_custom); - if (versionfile.exists()) - verpath = verpath_custom; - } - - auto version = OneSixVersion::fromFile(verpath); - if (version) - { - d->version = version; - return true; - } - else - { - d->version.reset(); - return false; - } -} - -std::shared_ptr OneSixInstance::getFullVersion() -{ - I_D(OneSixInstance); + I_D(DerpInstance); return d->version; } -QString OneSixInstance::defaultBaseJar() const +QString DerpInstance::defaultBaseJar() const { return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; } -QString OneSixInstance::defaultCustomBaseJar() const +QString DerpInstance::defaultCustomBaseJar() const { return PathCombine(instanceRoot(), "custom.jar"); } -bool OneSixInstance::menuActionEnabled(QString action_name) const +bool DerpInstance::menuActionEnabled(QString action_name) const { if (action_name == "actionChangeInstLWJGLVersion") return false; return true; } -QString OneSixInstance::getStatusbarDescription() +QString DerpInstance::getStatusbarDescription() { - QString descr = "One Six : " + intendedVersionId(); + QString descr = "Derp : " + intendedVersionId(); if (versionIsCustom()) { descr + " (custom)"; @@ -400,17 +357,17 @@ QString OneSixInstance::getStatusbarDescription() return descr; } -QString OneSixInstance::loaderModsDir() const +QString DerpInstance::loaderModsDir() const { return PathCombine(minecraftRoot(), "mods"); } -QString OneSixInstance::resourcePacksDir() const +QString DerpInstance::resourcePacksDir() const { return PathCombine(minecraftRoot(), "resourcepacks"); } -QString OneSixInstance::instanceConfigFolder() const +QString DerpInstance::instanceConfigFolder() const { return PathCombine(minecraftRoot(), "config"); } diff --git a/logic/OneSixInstance.h b/logic/DerpInstance.h similarity index 76% rename from logic/OneSixInstance.h rename to logic/DerpInstance.h index f869e345..37d3df52 100644 --- a/logic/OneSixInstance.h +++ b/logic/DerpInstance.h @@ -15,21 +15,17 @@ #pragma once -#include -#include - #include "BaseInstance.h" -class OneSixVersion; -class Task; -class ModList; +#include "DerpVersion.h" +#include "ModList.h" -class OneSixInstance : public BaseInstance +class DerpInstance : public BaseInstance { Q_OBJECT public: - explicit OneSixInstance(const QString &rootDir, SettingsObject *settings, - QObject *parent = 0); + explicit DerpInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); ////// Mod Lists ////// std::shared_ptr loaderModList(); @@ -55,14 +51,10 @@ public: virtual QDialog *createModEditDialog(QWidget *parent) override; - /// reload the full version json file. return true on success! - bool reloadFullVersion(); + /// reload the full version json files. return true on success! + bool reloadFullVersion(QWidget *widgetParent = 0); /// get the current full version info - std::shared_ptr getFullVersion(); - /// revert the current custom version back to base - bool revertCustomVersion(); - /// customize the current base version - bool customizeVersion(); + std::shared_ptr getFullVersion(); /// is the current version original, or custom? virtual bool versionIsCustom() override; @@ -72,7 +64,10 @@ public: virtual bool menuActionEnabled(QString action_name) const override; virtual QString getStatusbarDescription() override; +signals: + void versionReloaded(); + private: QStringList processMinecraftArgs(MojangAccountPtr account); - QDir reconstructAssets(std::shared_ptr version); + QDir reconstructAssets(std::shared_ptr version); }; diff --git a/logic/OneSixInstance_p.h b/logic/DerpInstance_p.h similarity index 74% rename from logic/OneSixInstance_p.h rename to logic/DerpInstance_p.h index 6b7ea431..41f7b62d 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/DerpInstance_p.h @@ -15,16 +15,13 @@ #pragma once -#include +#include "BaseInstance_p.h" +#include "DerpVersion.h" +#include "ModList.h" -#include "logic/BaseInstance_p.h" -#include "logic/OneSixVersion.h" -#include "logic/OneSixLibrary.h" -#include "logic/ModList.h" - -struct OneSixInstancePrivate : public BaseInstancePrivate +struct DerpInstancePrivate : public BaseInstancePrivate { - std::shared_ptr version; + std::shared_ptr version; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; -}; \ No newline at end of file +}; diff --git a/logic/OneSixLibrary.cpp b/logic/DerpLibrary.cpp similarity index 87% rename from logic/OneSixLibrary.cpp rename to logic/DerpLibrary.cpp index 7b80d5e7..ba4d516b 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/DerpLibrary.cpp @@ -15,15 +15,15 @@ #include -#include "OneSixLibrary.h" -#include "OneSixRule.h" +#include "DerpLibrary.h" +#include "DerpRule.h" #include "OpSys.h" #include "logic/net/URLConstants.h" #include #include #include "logger/QsLog.h" -void OneSixLibrary::finalize() +void DerpLibrary::finalize() { QStringList parts = m_name.split(':'); QString relative = parts[0]; @@ -76,67 +76,67 @@ void OneSixLibrary::finalize() } } -void OneSixLibrary::setName(QString name) +void DerpLibrary::setName(const QString &name) { m_name = name; } -void OneSixLibrary::setBaseUrl(QString base_url) +void DerpLibrary::setBaseUrl(const QString &base_url) { m_base_url = base_url; } -void OneSixLibrary::setIsNative() +void DerpLibrary::setIsNative() { m_is_native = true; } -void OneSixLibrary::addNative(OpSys os, QString suffix) +void DerpLibrary::addNative(OpSys os, const QString &suffix) { m_is_native = true; m_native_suffixes[os] = suffix; } -void OneSixLibrary::setRules(QList> rules) +void DerpLibrary::setRules(QList> rules) { m_rules = rules; } -bool OneSixLibrary::isActive() +bool DerpLibrary::isActive() const { return m_is_active; } -bool OneSixLibrary::isNative() +bool DerpLibrary::isNative() const { return m_is_native; } -QString OneSixLibrary::downloadUrl() +QString DerpLibrary::downloadUrl() const { if (m_absolute_url.size()) return m_absolute_url; return m_download_url; } -QString OneSixLibrary::storagePath() +QString DerpLibrary::storagePath() const { return m_storage_path; } -void OneSixLibrary::setAbsoluteUrl(QString absolute_url) +void DerpLibrary::setAbsoluteUrl(const QString &absolute_url) { m_absolute_url = absolute_url; } -QString OneSixLibrary::absoluteUrl() +QString DerpLibrary::absoluteUrl() const { return m_absolute_url; } -void OneSixLibrary::setHint(QString hint) +void DerpLibrary::setHint(const QString &hint) { m_hint = hint; } -QString OneSixLibrary::hint() +QString DerpLibrary::hint() const { return m_hint; } -bool OneSixLibrary::filesExist() +bool DerpLibrary::filesExist() { QString storage = storagePath(); if (storage.contains("${arch}")) @@ -167,7 +167,7 @@ bool OneSixLibrary::filesExist() return true; } -bool OneSixLibrary::extractTo(QString target_dir) +bool DerpLibrary::extractTo(QString target_dir) { QString storage = storagePath(); if (storage.contains("${arch}")) @@ -220,7 +220,7 @@ bool OneSixLibrary::extractTo(QString target_dir) return true; } -QJsonObject OneSixLibrary::toJson() +QJsonObject DerpLibrary::toJson() { QJsonObject libRoot; libRoot.insert("name", m_name); diff --git a/logic/OneSixLibrary.h b/logic/DerpLibrary.h similarity index 85% rename from logic/OneSixLibrary.h rename to logic/DerpLibrary.h index 227cdbef..d1cee843 100644 --- a/logic/OneSixLibrary.h +++ b/logic/DerpLibrary.h @@ -26,7 +26,7 @@ class Rule; -class OneSixLibrary +class DerpLibrary { private: // basic values used internally (so far) @@ -63,7 +63,7 @@ public: public: /// Constructor - OneSixLibrary(QString name) + DerpLibrary(const QString &name) { m_name = name; } @@ -84,48 +84,48 @@ public: void finalize(); /// Set the library composite name - void setName(QString name); + void setName(const QString &name); /// get a decent-looking name - QString name() + QString name() const { return m_decentname; } /// get a decent-looking version - QString version() + QString version() const { return m_decentversion; } /// what kind of library is it? (for display) - QString type() + QString type() const { return m_decenttype; } /// Set the url base for downloads - void setBaseUrl(QString base_url); + void setBaseUrl(const QString &base_url); /// Call this to mark the library as 'native' (it's a zip archive with DLLs) void setIsNative(); /// Attach a name suffix to the specified OS native - void addNative(OpSys os, QString suffix); + void addNative(OpSys os, const QString &suffix); /// Set the load rules void setRules(QList> rules); /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive(); + bool isActive() const; /// Returns true if the library is native - bool isNative(); + bool isNative() const; /// Get the URL to download the library from - QString downloadUrl(); + QString downloadUrl() const; /// Get the relative path where the library should be saved - QString storagePath(); + QString storagePath() const; /// set an absolute URL for the library. This is an MMC extension. - void setAbsoluteUrl(QString absolute_url); - QString absoluteUrl(); + void setAbsoluteUrl(const QString &absolute_url); + QString absoluteUrl() const; /// set a hint about how to treat the library. This is an MMC extension. - void setHint(QString hint); - QString hint(); + void setHint(const QString &hint); + QString hint() const; bool extractTo(QString target_dir); bool filesExist(); diff --git a/logic/OneSixRule.cpp b/logic/DerpRule.cpp similarity index 98% rename from logic/OneSixRule.cpp rename to logic/DerpRule.cpp index 392b1dd1..d4cf1ba3 100644 --- a/logic/OneSixRule.cpp +++ b/logic/DerpRule.cpp @@ -16,7 +16,7 @@ #include #include -#include "OneSixRule.h" +#include "DerpRule.h" QList> rulesFromJsonV4(QJsonObject &objectWithRules) { @@ -86,4 +86,4 @@ RuleAction RuleAction_fromString(QString name) if (name == "disallow") return Disallow; return Defer; -} \ No newline at end of file +} diff --git a/logic/OneSixRule.h b/logic/DerpRule.h similarity index 90% rename from logic/OneSixRule.h rename to logic/DerpRule.h index 5a13cbd9..7895ea98 100644 --- a/logic/OneSixRule.h +++ b/logic/DerpRule.h @@ -17,7 +17,7 @@ #include -#include "logic/OneSixLibrary.h" +#include "logic/DerpLibrary.h" enum RuleAction { @@ -33,7 +33,7 @@ class Rule { protected: RuleAction m_result; - virtual bool applies(OneSixLibrary *parent) = 0; + virtual bool applies(DerpLibrary *parent) = 0; public: Rule(RuleAction result) : m_result(result) @@ -41,7 +41,7 @@ public: } virtual ~Rule() {}; virtual QJsonObject toJson() = 0; - RuleAction apply(OneSixLibrary *parent) + RuleAction apply(DerpLibrary *parent) { if (applies(parent)) return m_result; @@ -60,7 +60,7 @@ private: QString m_version_regexp; protected: - virtual bool applies(OneSixLibrary *) + virtual bool applies(DerpLibrary *) { return (m_system == currentSystem); } @@ -81,7 +81,7 @@ public: class ImplicitRule : public Rule { protected: - virtual bool applies(OneSixLibrary *) + virtual bool applies(DerpLibrary *) { return true; } diff --git a/logic/OneSixUpdate.cpp b/logic/DerpUpdate.cpp similarity index 88% rename from logic/OneSixUpdate.cpp rename to logic/DerpUpdate.cpp index 0119ab07..e1600d28 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/DerpUpdate.cpp @@ -14,7 +14,7 @@ */ #include "MultiMC.h" -#include "OneSixUpdate.h" +#include "DerpUpdate.h" #include @@ -25,9 +25,9 @@ #include "BaseInstance.h" #include "lists/MinecraftVersionList.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" -#include "OneSixInstance.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" +#include "DerpInstance.h" #include "net/ForgeMirrors.h" #include "net/URLConstants.h" #include "assets/AssetsUtils.h" @@ -35,12 +35,12 @@ #include "pathutils.h" #include -OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) +DerpUpdate::DerpUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) : Task(parent), m_inst(inst), m_only_prepare(only_prepare) { } -void OneSixUpdate::executeTask() +void DerpUpdate::executeTask() { QString intendedVersion = m_inst->intendedVersionId(); @@ -77,7 +77,7 @@ void OneSixUpdate::executeTask() } } -void OneSixUpdate::versionFileStart() +void DerpUpdate::versionFileStart() { QLOG_INFO() << m_inst->name() << ": getting version file."; setStatus(tr("Getting the version files from Mojang...")); @@ -94,10 +94,10 @@ void OneSixUpdate::versionFileStart() specificVersionDownloadJob->start(); } -void OneSixUpdate::versionFileFinished() +void DerpUpdate::versionFileFinished() { NetActionPtr DlJob = specificVersionDownloadJob->first(); - OneSixInstance *inst = (OneSixInstance *)m_inst; + DerpInstance *inst = (DerpInstance *)m_inst; QString version_id = targetVersion->descriptor(); QString inst_dir = m_inst->instanceRoot(); @@ -142,16 +142,16 @@ void OneSixUpdate::versionFileFinished() jarlibStart(); } -void OneSixUpdate::versionFileFailed() +void DerpUpdate::versionFileFailed() { emitFailed("Failed to download the version description. Try again."); } -void OneSixUpdate::assetIndexStart() +void DerpUpdate::assetIndexStart() { setStatus(tr("Updating assets index...")); - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); + DerpInstance *inst = (DerpInstance *)m_inst; + std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; QString localPath = assetName + ".json"; @@ -170,12 +170,12 @@ void OneSixUpdate::assetIndexStart() jarlibDownloadJob->start(); } -void OneSixUpdate::assetIndexFinished() +void DerpUpdate::assetIndexFinished() { AssetsIndex index; - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); + DerpInstance *inst = (DerpInstance *)m_inst; + std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; QString asset_fname = "assets/indexes/" + assetName + ".json"; @@ -215,26 +215,26 @@ void OneSixUpdate::assetIndexFinished() assetsFinished(); } -void OneSixUpdate::assetIndexFailed() +void DerpUpdate::assetIndexFailed() { emitFailed("Failed to download the assets index!"); } -void OneSixUpdate::assetsFinished() +void DerpUpdate::assetsFinished() { prepareForLaunch(); } -void OneSixUpdate::assetsFailed() +void DerpUpdate::assetsFailed() { emitFailed("Failed to download assets!"); } -void OneSixUpdate::jarlibStart() +void DerpUpdate::jarlibStart() { setStatus(tr("Getting the library files from Mojang...")); QLOG_INFO() << m_inst->name() << ": downloading libraries"; - OneSixInstance *inst = (OneSixInstance *)m_inst; + DerpInstance *inst = (DerpInstance *)m_inst; bool successful = inst->reloadFullVersion(); if (!successful) { @@ -244,7 +244,7 @@ void OneSixUpdate::jarlibStart() } // Build a list of URLs that will need to be downloaded. - std::shared_ptr version = inst->getFullVersion(); + std::shared_ptr version = inst->getFullVersion(); // minecraft.jar for this version { QString version_id = version->id; @@ -318,12 +318,12 @@ void OneSixUpdate::jarlibStart() jarlibDownloadJob->start(); } -void OneSixUpdate::jarlibFinished() +void DerpUpdate::jarlibFinished() { assetIndexStart(); } -void OneSixUpdate::jarlibFailed() +void DerpUpdate::jarlibFailed() { QStringList failed = jarlibDownloadJob->getFailedFiles(); QString failed_all = failed.join("\n"); @@ -331,17 +331,17 @@ void OneSixUpdate::jarlibFailed() "\n\nPlease try again."); } -void OneSixUpdate::prepareForLaunch() +void DerpUpdate::prepareForLaunch() { setStatus(tr("Preparing for launch...")); QLOG_INFO() << m_inst->name() << ": preparing for launch"; - auto onesix_inst = (OneSixInstance *)m_inst; + auto derp_inst = (DerpInstance *)m_inst; // delete any leftovers, if they are present. - onesix_inst->cleanupAfterRun(); + derp_inst->cleanupAfterRun(); - QString natives_dir_raw = PathCombine(onesix_inst->instanceRoot(), "natives/"); - auto version = onesix_inst->getFullVersion(); + QString natives_dir_raw = PathCombine(derp_inst->instanceRoot(), "natives/"); + auto version = derp_inst->getFullVersion(); if (!version) { emitFailed("The version information for this instance is not complete. Try re-creating " diff --git a/logic/OneSixUpdate.h b/logic/DerpUpdate.h similarity index 92% rename from logic/OneSixUpdate.h rename to logic/DerpUpdate.h index bc717a94..475f6c35 100644 --- a/logic/OneSixUpdate.h +++ b/logic/DerpUpdate.h @@ -25,11 +25,11 @@ class MinecraftVersion; class BaseInstance; -class OneSixUpdate : public Task +class DerpUpdate : public Task { Q_OBJECT public: - explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); + explicit DerpUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); virtual void executeTask(); private diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp new file mode 100644 index 00000000..f13ac620 --- /dev/null +++ b/logic/DerpVersion.cpp @@ -0,0 +1,164 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 "DerpVersion.h" + +#include + +#include "DerpVersionBuilder.h" + +DerpVersion::DerpVersion(DerpInstance *instance, QObject *parent) + : QAbstractListModel(parent), m_instance(instance) +{ +} + +bool DerpVersion::reload(QWidget *widgetParent) +{ + return DerpVersionBuilder::build(this, m_instance, widgetParent); +} + +QList > DerpVersion::getActiveNormalLibs() +{ + QList > output; + for (auto lib : libraries) + { + if (lib->isActive() && !lib->isNative()) + { + output.append(lib); + } + } + return output; +} + +QList > DerpVersion::getActiveNativeLibs() +{ + QList > output; + for (auto lib : libraries) + { + if (lib->isActive() && lib->isNative()) + { + output.append(lib); + } + } + return output; +} + +QVariant DerpVersion::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + int column = index.column(); + + if (row < 0 || row >= libraries.size()) + return QVariant(); + + if (role == Qt::DisplayRole) + { + switch (column) + { + case 0: + return libraries[row]->name(); + case 1: + return libraries[row]->type(); + case 2: + return libraries[row]->version(); + default: + return QVariant(); + } + } + return QVariant(); +} + +Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + int row = index.row(); + if (libraries[row]->isActive()) + { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; + } + else + { + return Qt::ItemNeverHasChildren; + } + // return QAbstractListModel::flags(index); +} + +QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + switch (section) + { + case 0: + return QString("Name"); + case 1: + return QString("Type"); + case 2: + return QString("Version"); + default: + return QString(); + } +} + +int DerpVersion::rowCount(const QModelIndex &parent) const +{ + return libraries.size(); +} + +int DerpVersion::columnCount(const QModelIndex &parent) const +{ + return 3; +} + +QDebug operator<<(QDebug &dbg, const DerpVersion *version) +{ + dbg.nospace() << "DerpVersion(" + << "\n\tid=" << version->id + << "\n\ttime=" << version->time + << "\n\treleaseTime=" << version->releaseTime + << "\n\ttype=" << version->type + << "\n\tassets=" << version->assets + << "\n\tprocessArguments=" << version->processArguments + << "\n\tminecraftArguments=" << version->minecraftArguments + << "\n\tminimumLauncherVersion=" << version->minimumLauncherVersion + << "\n\tmainClass=" << version->mainClass + << "\n\tlibraries="; + for (auto lib : version->libraries) + { + dbg.nospace() << "\n\t\t" << lib.get(); + } + dbg.nospace() << "\n)"; + return dbg.maybeSpace(); +} +QDebug operator<<(QDebug &dbg, const DerpLibrary *library) +{ + dbg.nospace() << "DerpLibrary(" + << "\n\t\t\trawName=" << library->rawName() + << "\n\t\t\tname=" << library->name() + << "\n\t\t\tversion=" << library->version() + << "\n\t\t\ttype=" << library->type() + << "\n\t\t\tisActive=" << library->isActive() + << "\n\t\t\tisNative=" << library->isNative() + << "\n\t\t\tdownloadUrl=" << library->downloadUrl() + << "\n\t\t\tstoragePath=" << library->storagePath() + << "\n\t\t\tabsolutePath=" << library->absoluteUrl() + << "\n\t\t\thint=" << library->hint(); + dbg.nospace() << "\n\t\t)"; + return dbg.maybeSpace(); +} diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h new file mode 100644 index 00000000..cadfa850 --- /dev/null +++ b/logic/DerpVersion.h @@ -0,0 +1,110 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 + +#include +#include +#include + +#include "DerpLibrary.h" + +class DerpInstance; + +class DerpVersion : public QAbstractListModel +{ + Q_OBJECT +public: + explicit DerpVersion(DerpInstance *instance, QObject *parent = 0); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex &parent) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + bool reload(QWidget *widgetParent); + +public: + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); + + // data members +public: + /// the ID - determines which jar to use! ACTUALLY IMPORTANT! + QString id; + /// Last updated time - as a string + QString time; + /// Release time - as a string + QString releaseTime; + /// Release type - "release" or "snapshot" + QString type; + /// Assets type - "legacy" or a version ID + QString assets; + /** + * DEPRECATED: Old versions of the new vanilla launcher used this + * ex: "username_session_version" + */ + QString processArguments; + /** + * arguments that should be used for launching minecraft + * + * ex: "--username ${auth_player_name} --session ${auth_session} + * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" + */ + QString minecraftArguments; + /** + * the minimum launcher version required by this version ... current is 4 (at point of + * writing) + */ + int minimumLauncherVersion = 0xDEADBEEF; + /** + * The main class to load first + */ + QString mainClass; + + /// the list of libs - both active and inactive, native and java + QList> libraries; + + /* + FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. + + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "osx", + "version": "^10\\.5\\.\\d$" + } + } + ], + "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX + 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" + } + */ + // QList rules; + +private: + DerpInstance *m_instance; +}; + +QDebug operator<<(QDebug &dbg, const DerpVersion *version); +QDebug operator<<(QDebug &dbg, const DerpLibrary *library); diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp new file mode 100644 index 00000000..d8091f32 --- /dev/null +++ b/logic/DerpVersionBuilder.cpp @@ -0,0 +1,279 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 "DerpVersionBuilder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DerpVersion.h" +#include "DerpInstance.h" +#include "DerpRule.h" + +DerpVersionBuilder::DerpVersionBuilder() +{ + +} + +bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent) +{ + DerpVersionBuilder builder; + builder.m_version = version; + builder.m_instance = instance; + builder.m_widgetParent = widgetParent; + return builder.build(); +} + +bool DerpVersionBuilder::build() +{ + clear(); + + QDir root(m_instance->instanceRoot()); + QDir patches(root.absoluteFilePath("patches/")); + + // version.json -> patches/*.json -> custom.json + + // version.json + { + QJsonObject obj; + if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) + { + return false; + } + if (!apply(obj)) + { + return false; + } + } + + // patches/ + { + // load all, put into map for ordering, apply in the right order + + QMap objects; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + { + QJsonObject obj; + if (!read(info, &obj)) + { + return false; + } + if (!obj.contains("order") || !obj.value("order").isDouble()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); + return false; + } + objects.insert(obj.value("order").toDouble(), obj); + } + for (auto object : objects.values()) + { + if (!apply(object)) + { + return false; + } + } + } + + // custom.json + { + if (QFile::exists(root.absoluteFilePath("custom.json"))) + { + QJsonObject obj; + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) + { + return false; + } + if (!apply(obj)) + { + return false; + } + } + } + + return true; +} + +void DerpVersionBuilder::clear() +{ + m_version->id.clear(); + m_version->time.clear(); + m_version->releaseTime.clear(); + m_version->type.clear(); + m_version->assets.clear(); + m_version->processArguments.clear(); + m_version->minecraftArguments.clear(); + m_version->minimumLauncherVersion = 0xDEADBEAF; + m_version->mainClass.clear(); + m_version->libraries.clear(); +} + +void applyString(const QJsonObject &obj, const QString &key, QString &out) +{ + if (obj.contains(key) && obj.value(key).isString()) + { + out = obj.value(key).toString(); + } +} +void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(DerpLibrary::*func)(const QString &val)) +{ + if (obj.contains(key) && obj.value(key).isString()) + { + (lib.get()->*func)(obj.value(key).toString()); + } +} +bool DerpVersionBuilder::apply(const QJsonObject &object) +{ + applyString(object, "id", m_version->id); + applyString(object, "mainClass", m_version->mainClass); + applyString(object, "processArguments", m_version->processArguments); + { + const QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") + { + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; + } + else if (toCompare == "username_session") + { + m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; + } + } + applyString(object, "minecraftArguments", m_version->minecraftArguments); + applyString(object, "type", m_version->type); + applyString(object, "releaseTime", m_version->releaseTime); + applyString(object, "time", m_version->time); + applyString(object, "assets", m_version->assets); + { + if (m_version->assets.isEmpty()) + { + m_version->assets = "legacy"; + } + } + if (object.contains("minimumLauncherVersion")) + { + auto minLauncherVersionVal = object.value("minimumLauncherVersion"); + if (minLauncherVersionVal.isDouble()) + { + m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); + } + } + + // libraries + { + auto librariesValue = object.value("libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + for (auto libVal : librariesValue.toArray()) + { + if (!libVal.isObject()) + { + continue; + } + + QJsonObject libObj = libVal.toObject(); + + // Library name + auto nameVal = libObj.value("name"); + if (!nameVal.isString()) + { + continue; + } + std::shared_ptr library(new DerpLibrary(nameVal.toString())); + + applyString(libObj, "url", library, &DerpLibrary::setBaseUrl); + applyString(libObj, "MMC-hint", library, &DerpLibrary::setHint); + applyString(libObj, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); + applyString(libObj, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); + + auto extractVal = libObj.value("extract"); + if (extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if (excludesVal.isArray()) + { + auto excludesList = excludesVal.toArray(); + for (auto excludeVal : excludesList) + { + if (excludeVal.isString()) + { + excludes.append(excludeVal.toString()); + } + } + library->extract_excludes = excludes; + } + } + + auto nativesVal = libObj.value("natives"); + if (nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + auto osType = OpSys_fromString(it.key()); + if (osType == Os_Other) + { + continue; + } + if (!it.value().isString()) + { + continue; + } + library->addNative(osType, it.value().toString()); + } + } + + library->setRules(rulesFromJsonV4(libObj)); + library->finalize(); + m_version->libraries.append(library); + } + } + + return true; +} + +bool DerpVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +{ + QFile file(fileInfo.absoluteFilePath()); + if (!file.open(QFile::ReadOnly)) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); + return false; + } + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); + if (error.error != QJsonParseError::NoError) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset)); + return false; + } + *out = doc.object(); + return true; +} diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h new file mode 100644 index 00000000..739065db --- /dev/null +++ b/logic/DerpVersionBuilder.h @@ -0,0 +1,43 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 + +class DerpVersion; +class DerpInstance; +class QWidget; +class QJsonObject; +class QFileInfo; + +class DerpVersionBuilder +{ + DerpVersionBuilder(); +public: + static bool build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent); + +private: + DerpVersion *m_version; + DerpInstance *m_instance; + QWidget *m_widgetParent; + + bool build(); + + void clear(); + bool apply(const QJsonObject &object); + + bool read(const QFileInfo &fileInfo, QJsonObject *out); +}; diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 8d4c5b41..29735a7d 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -14,8 +14,8 @@ */ #include "ForgeInstaller.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" #include "net/HttpMetaCache.h" #include #include @@ -23,9 +23,15 @@ #include #include "MultiMC.h" +#include +#include +#include + +// DERPFIX + ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - std::shared_ptr newVersion; + std::shared_ptr newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -58,7 +64,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); + // DERPFIX newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } @@ -68,7 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) internalPath = installObj.value("filePath").toString(); // where do we put the library? decode the mojang path - OneSixLibrary lib(libraryName); + DerpLibrary lib(libraryName); lib.finalize(); auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath()); @@ -103,11 +109,10 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::apply(std::shared_ptr to) +bool ForgeInstaller::apply(std::shared_ptr to) { if (!m_forge_version) return false; - to->externalUpdateStart(); int sliding_insert_window = 0; { // for each library in the version we are adding (except for the blacklisted) @@ -150,6 +155,5 @@ bool ForgeInstaller::apply(std::shared_ptr to) to->minecraftArguments = m_forge_version->minecraftArguments; to->processArguments = m_forge_version->processArguments; } - to->externalUpdateFinish(); - return to->toOriginalFile(); + return true; } diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index 0b9f9c77..65cad87c 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -17,18 +17,18 @@ #include #include -class OneSixVersion; +class DerpVersion; class ForgeInstaller { public: ForgeInstaller(QString filename, QString universal_url); - bool apply(std::shared_ptr to); + bool apply(std::shared_ptr to); private: // the version, read from the installer - std::shared_ptr m_forge_version; + std::shared_ptr m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 1f1a5879..c50ec4e4 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -21,9 +21,10 @@ #include "BaseInstance.h" #include "LegacyInstance.h" #include "LegacyFTBInstance.h" -#include "OneSixInstance.h" -#include "OneSixFTBInstance.h" +#include "DerpInstance.h" +#include "DerpFTBInstance.h" #include "NostalgiaInstance.h" +#include "DerpInstance.h" #include "BaseVersion.h" #include "MinecraftVersion.h" @@ -50,14 +51,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst QString inst_type = m_settings->get("InstanceType").toString(); // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "Legacy") + if (inst_type == "Derp" || inst_type == "OneSix") + { + inst = new DerpInstance(instDir, m_settings, this); + } + else if (inst_type == "Legacy") { inst = new LegacyInstance(instDir, m_settings, this); } - else if (inst_type == "OneSix") - { - inst = new OneSixInstance(instDir, m_settings, this); - } else if (inst_type == "Nostalgia") { inst = new NostalgiaInstance(instDir, m_settings, this); @@ -66,9 +67,9 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new LegacyFTBInstance(instDir, m_settings, this); } - else if (inst_type == "OneSixFTB") + else if (inst_type == "OneSixFTB" || inst_type == "DerpFTB") { - inst = new OneSixFTBInstance(instDir, m_settings, this); + inst = new DerpFTBInstance(instDir, m_settings, this); } else { @@ -101,14 +102,15 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& switch (mcVer->type) { case MinecraftVersion::Legacy: + // TODO derp m_settings->set("InstanceType", "Legacy"); inst = new LegacyInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::OneSix: - m_settings->set("InstanceType", "OneSix"); - inst = new OneSixInstance(instDir, m_settings, this); + case MinecraftVersion::Derp: + m_settings->set("InstanceType", "Derp"); + inst = new DerpInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -135,9 +137,9 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::OneSix: - m_settings->set("InstanceType", "OneSixFTB"); - inst = new OneSixFTBInstance(instDir, m_settings, this); + case MinecraftVersion::Derp: + m_settings->set("InstanceType", "DerpFTB"); + inst = new DerpFTBInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -174,8 +176,8 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); - if(inst_type == "OneSixFTB") - m_settings->set("InstanceType", "OneSix"); + if(inst_type == "OneSixFTB" || inst_type == "DerpFTB") + m_settings->set("InstanceType", "Derp"); if(inst_type == "LegacyFTB") m_settings->set("InstanceType", "Legacy"); diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 1e7d9eb6..1ff17cbd 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -68,7 +68,7 @@ public: virtual QString intendedVersionId() const override; virtual bool setIntendedVersionId(QString version) override; // the `version' of Legacy instances is defined by the launcher code. - // in contrast with OneSix, where `version' is described in a json file + // in contrast with Derp, where `version' is described in a json file virtual bool versionIsCustom() override { return false; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 07fffff3..8a120eab 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -15,8 +15,8 @@ #include "LiteLoaderInstaller.h" -#include "OneSixVersion.h" -#include "OneSixLibrary.h" +#include "DerpVersion.h" +#include "DerpLibrary.h" QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; @@ -36,9 +36,9 @@ bool LiteLoaderInstaller::canApply() const return m_launcherWrapperVersionMapping.contains(m_mcVersion); } -bool LiteLoaderInstaller::apply(std::shared_ptr to) +bool LiteLoaderInstaller::apply(std::shared_ptr to) { - to->externalUpdateStart(); + // DERPFIX applyLaunchwrapper(to); applyLiteLoader(to); @@ -51,15 +51,14 @@ bool LiteLoaderInstaller::apply(std::shared_ptr to) " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); } - to->externalUpdateFinish(); - return to->toOriginalFile(); + return true; } -void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) +void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) { const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion]; - QMutableListIterator> it(to->libraries); + QMutableListIterator> it(to->libraries); while (it.hasNext()) { it.next(); @@ -76,15 +75,15 @@ void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) } } - std::shared_ptr lib(new OneSixLibrary( + std::shared_ptr lib(new DerpLibrary( "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion])); lib->finalize(); to->libraries.prepend(lib); } -void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) +void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) { - QMutableListIterator> it(to->libraries); + QMutableListIterator> it(to->libraries); while (it.hasNext()) { it.next(); @@ -94,8 +93,8 @@ void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) } } - std::shared_ptr lib( - new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion)); + std::shared_ptr lib( + new DerpLibrary("com.mumfrey:liteloader:" + m_mcVersion)); lib->setBaseUrl("http://dl.liteloader.com/versions/"); lib->finalize(); to->libraries.prepend(lib); diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index 44b306d6..d7b8ce77 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -18,7 +18,7 @@ #include #include -class OneSixVersion; +class DerpVersion; class LiteLoaderInstaller { @@ -27,13 +27,13 @@ public: bool canApply() const; - bool apply(std::shared_ptr to); + bool apply(std::shared_ptr to); private: QString m_mcVersion; - void applyLaunchwrapper(std::shared_ptr to); - void applyLiteLoader(std::shared_ptr to); + void applyLaunchwrapper(std::shared_ptr to); + void applyLiteLoader(std::shared_ptr to); static QMap m_launcherWrapperVersionMapping; }; diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h index 504381a8..dd849ce9 100644 --- a/logic/MinecraftVersion.h +++ b/logic/MinecraftVersion.h @@ -32,7 +32,7 @@ struct MinecraftVersion : public BaseVersion /// This version's type. Used internally to identify what kind of version this is. enum VersionType { - OneSix, + Derp, Legacy, Nostalgia } type; @@ -66,8 +66,8 @@ struct MinecraftVersion : public BaseVersion } switch (type) { - case OneSix: - pre_final.append("OneSix"); + case Derp: + pre_final.append("Derp"); break; case Legacy: pre_final.append("Legacy"); diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp index 2e23ee71..f27954a7 100644 --- a/logic/NostalgiaInstance.cpp +++ b/logic/NostalgiaInstance.cpp @@ -17,7 +17,7 @@ NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) - : OneSixInstance(rootDir, settings, parent) + : DerpInstance(rootDir, settings, parent) { } diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h index a26f7f0a..6a6b91a1 100644 --- a/logic/NostalgiaInstance.h +++ b/logic/NostalgiaInstance.h @@ -15,9 +15,9 @@ #pragma once -#include "OneSixInstance.h" +#include "DerpInstance.h" -class NostalgiaInstance : public OneSixInstance +class NostalgiaInstance : public DerpInstance { Q_OBJECT public: diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 8ae685f0..3571be9b 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -13,14 +13,14 @@ * limitations under the License. */ -#include "logic/OneSixVersion.h" -#include "logic/OneSixLibrary.h" -#include "logic/OneSixRule.h" +#include "logic/DerpVersion.h" +#include "logic/DerpLibrary.h" +#include "logic/DerpRule.h" #include "logger/QsLog.h" -std::shared_ptr fromJsonV4(QJsonObject root, - std::shared_ptr fullVersion) +std::shared_ptr fromJsonV4(QJsonObject root, + std::shared_ptr fullVersion) { fullVersion->id = root.value("id").toString(); @@ -93,7 +93,7 @@ std::shared_ptr fromJsonV4(QJsonObject root, auto nameVal = libObj.value("name"); if (!nameVal.isString()) continue; - std::shared_ptr library(new OneSixLibrary(nameVal.toString())); + std::shared_ptr library(new DerpLibrary(nameVal.toString())); auto urlVal = libObj.value("url"); if (urlVal.isString()) @@ -158,9 +158,9 @@ std::shared_ptr fromJsonV4(QJsonObject root, return fullVersion; } -std::shared_ptr OneSixVersion::fromJson(QJsonObject root) +std::shared_ptr DerpVersion::fromJson(QJsonObject root) { - std::shared_ptr readVersion(new OneSixVersion()); + std::shared_ptr readVersion(new DerpVersion()); int launcher_ver = readVersion->minimumLauncherVersion = root.value("minimumLauncherVersion").toDouble(); @@ -169,16 +169,16 @@ std::shared_ptr OneSixVersion::fromJson(QJsonObject root) return fromJsonV4(root, readVersion); else { - return std::shared_ptr(); + return std::shared_ptr(); } } -std::shared_ptr OneSixVersion::fromFile(QString filepath) +std::shared_ptr DerpVersion::fromFile(QString filepath) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { - return std::shared_ptr(); + return std::shared_ptr(); } auto data = file.readAll(); @@ -187,12 +187,12 @@ std::shared_ptr OneSixVersion::fromFile(QString filepath) if (jsonError.error != QJsonParseError::NoError) { - return std::shared_ptr(); + return std::shared_ptr(); } if (!jsonDoc.isObject()) { - return std::shared_ptr(); + return std::shared_ptr(); } QJsonObject root = jsonDoc.object(); auto version = fromJson(root); @@ -201,7 +201,7 @@ std::shared_ptr OneSixVersion::fromFile(QString filepath) return version; } -bool OneSixVersion::toOriginalFile() +bool DerpVersion::toOriginalFile() { if (original_file.isEmpty()) return false; @@ -232,9 +232,9 @@ bool OneSixVersion::toOriginalFile() return file.commit(); } -QList> OneSixVersion::getActiveNormalLibs() +QList> DerpVersion::getActiveNormalLibs() { - QList> output; + QList> output; for (auto lib : libraries) { if (lib->isActive() && !lib->isNative()) @@ -245,9 +245,9 @@ QList> OneSixVersion::getActiveNormalLibs() return output; } -QList> OneSixVersion::getActiveNativeLibs() +QList> DerpVersion::getActiveNativeLibs() { - QList> output; + QList> output; for (auto lib : libraries) { if (lib->isActive() && lib->isNative()) @@ -258,17 +258,17 @@ QList> OneSixVersion::getActiveNativeLibs() return output; } -void OneSixVersion::externalUpdateStart() +void DerpVersion::externalUpdateStart() { beginResetModel(); } -void OneSixVersion::externalUpdateFinish() +void DerpVersion::externalUpdateFinish() { endResetModel(); } -QVariant OneSixVersion::data(const QModelIndex &index, int role) const +QVariant DerpVersion::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -296,7 +296,7 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const return QVariant(); } -Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const +Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; @@ -312,7 +312,7 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const // return QAbstractListModel::flags(index); } -QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const +QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); @@ -329,12 +329,12 @@ QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int } } -int OneSixVersion::rowCount(const QModelIndex &parent) const +int DerpVersion::rowCount(const QModelIndex &parent) const { return libraries.size(); } -int OneSixVersion::columnCount(const QModelIndex &parent) const +int DerpVersion::columnCount(const QModelIndex &parent) const { return 3; } diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 036f3d53..e8f744aa 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -17,9 +17,9 @@ #include #include -class OneSixLibrary; +class DerpLibrary; -class OneSixVersion : public QAbstractListModel +class DerpVersion : public QAbstractListModel { // Things required to implement the Qt list model public: @@ -33,12 +33,12 @@ public: // serialization/deserialization public: bool toOriginalFile(); - static std::shared_ptr fromJson(QJsonObject root); - static std::shared_ptr fromFile(QString filepath); + static std::shared_ptr fromJson(QJsonObject root); + static std::shared_ptr fromFile(QString filepath); public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); // called when something starts/stops messing with the object // FIXME: these are ugly in every possible way. void externalUpdateStart(); @@ -81,7 +81,7 @@ public: QString mainClass; /// the list of libs - both active and inactive, native and java - QList> libraries; + QList> libraries; /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 91f86df0..29894b5a 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -234,18 +234,18 @@ void MCVListLoadTask::list_downloaded() } // Parse the type. MinecraftVersion::VersionType versionType; - // OneSix or Legacy. use filter to determine type + // Derp or Legacy. use filter to determine type if (versionTypeStr == "release") { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::OneSix; + : MinecraftVersion::Derp; is_latest = (versionID == latestReleaseID); is_snapshot = false; } else if (versionTypeStr == "snapshot") // It's a snapshot... yay { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::OneSix; + : MinecraftVersion::Derp; is_latest = (versionID == latestSnapshotID); is_snapshot = true; } From 13ac46bc185e861f9772f85adf757890dbb80a57 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 14:06:32 +0100 Subject: [PATCH 14/49] Fix launching --- logic/DerpInstance.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/logic/DerpInstance.cpp b/logic/DerpInstance.cpp index 31ed7c95..440c90a4 100644 --- a/logic/DerpInstance.cpp +++ b/logic/DerpInstance.cpp @@ -227,9 +227,7 @@ MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) QDir natives_dir(PathCombine(instanceRoot(), "natives/")); launchScript += "windowTitle " + windowTitle() + "\n"; launchScript += "natives " + natives_dir.absolutePath() + "\n"; - launchScript += "launch onesix"; - - qDebug() << launchScript; + launchScript += "launch onesix\n"; // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); From 0a592ab99bc68ad918f74206edf2a77cc257683c Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 15:20:48 +0100 Subject: [PATCH 15/49] Work towards liteloader support. Fix creating new instance --- gui/dialogs/DerpModEditDialog.cpp | 8 +- logic/DerpInstance.cpp | 22 ++++-- logic/DerpInstance.h | 2 + logic/DerpVersion.cpp | 15 ++++ logic/DerpVersion.h | 1 + logic/DerpVersionBuilder.cpp | 18 +---- logic/LiteLoaderInstaller.cpp | 123 +++++++++++++++++------------- logic/LiteLoaderInstaller.h | 18 +++-- 8 files changed, 123 insertions(+), 84 deletions(-) diff --git a/gui/dialogs/DerpModEditDialog.cpp b/gui/dialogs/DerpModEditDialog.cpp index be17404c..aafaa2e2 100644 --- a/gui/dialogs/DerpModEditDialog.cpp +++ b/gui/dialogs/DerpModEditDialog.cpp @@ -96,7 +96,7 @@ void DerpModEditDialog::updateVersionControls() { bool customVersion = m_inst->versionIsCustom(); ui->forgeBtn->setEnabled(true); - ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply()); + ui->liteloaderBtn->setEnabled(LiteLoaderInstaller().canApply(m_inst)); ui->customEditorBtn->setEnabled(customVersion); } @@ -165,8 +165,8 @@ void DerpModEditDialog::on_forgeBtn_clicked() void DerpModEditDialog::on_liteloaderBtn_clicked() { - LiteLoaderInstaller liteloader(m_inst->intendedVersionId()); - if (!liteloader.canApply()) + LiteLoaderInstaller liteloader; + if (!liteloader.canApply(m_inst)) { QMessageBox::critical( this, tr("LiteLoader"), @@ -174,7 +174,7 @@ void DerpModEditDialog::on_liteloaderBtn_clicked() "into this version of Minecraft")); return; } - if (!liteloader.apply(m_version)) + if (!liteloader.add(m_inst)) { QMessageBox::critical( this, tr("LiteLoader"), diff --git a/logic/DerpInstance.cpp b/logic/DerpInstance.cpp index 672ba6c4..e6b92b1f 100644 --- a/logic/DerpInstance.cpp +++ b/logic/DerpInstance.cpp @@ -35,10 +35,16 @@ DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QOb d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); d->version.reset(new DerpVersion(this, this)); - reloadFullVersion(); + if (QDir(instanceRoot()).exists("version.json")) + { + reloadFullVersion(); + } + else + { + clearFullVersion(); + } } - std::shared_ptr DerpInstance::doUpdate(bool only_prepare) { return std::shared_ptr(new DerpUpdate(this, only_prepare)); @@ -280,9 +286,8 @@ bool DerpInstance::setIntendedVersionId(QString version) { settings().set("IntendedVersion", version); setShouldUpdate(true); - auto pathOrig = PathCombine(instanceRoot(), "version.json"); - QFile::remove(pathOrig); - reloadFullVersion(); + QFile::remove(PathCombine(instanceRoot(), "version.json")); + clearFullVersion(); return true; } @@ -327,6 +332,13 @@ bool DerpInstance::reloadFullVersion(QWidget *widgetParent) return ret; } +void DerpInstance::clearFullVersion() +{ + I_D(DerpInstance); + d->version->clear(); + emit versionReloaded(); +} + std::shared_ptr DerpInstance::getFullVersion() { I_D(DerpInstance); diff --git a/logic/DerpInstance.h b/logic/DerpInstance.h index 37d3df52..46f953c0 100644 --- a/logic/DerpInstance.h +++ b/logic/DerpInstance.h @@ -53,6 +53,8 @@ public: /// reload the full version json files. return true on success! bool reloadFullVersion(QWidget *widgetParent = 0); + /// clears all version information in preparation for an update + void clearFullVersion(); /// get the current full version info std::shared_ptr getFullVersion(); /// is the current version original, or custom? diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp index f13ac620..cabb2b24 100644 --- a/logic/DerpVersion.cpp +++ b/logic/DerpVersion.cpp @@ -22,6 +22,7 @@ DerpVersion::DerpVersion(DerpInstance *instance, QObject *parent) : QAbstractListModel(parent), m_instance(instance) { + clear(); } bool DerpVersion::reload(QWidget *widgetParent) @@ -29,6 +30,20 @@ bool DerpVersion::reload(QWidget *widgetParent) return DerpVersionBuilder::build(this, m_instance, widgetParent); } +void DerpVersion::clear() +{ + id.clear(); + time.clear(); + releaseTime.clear(); + type.clear(); + assets.clear(); + processArguments.clear(); + minecraftArguments.clear(); + minimumLauncherVersion = 0xDEADBEAF; + mainClass.clear(); + libraries.clear(); +} + QList > DerpVersion::getActiveNormalLibs() { QList > output; diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h index cadfa850..f2132ad8 100644 --- a/logic/DerpVersion.h +++ b/logic/DerpVersion.h @@ -39,6 +39,7 @@ public: virtual Qt::ItemFlags flags(const QModelIndex &index) const; bool reload(QWidget *widgetParent); + void clear(); public: QList> getActiveNormalLibs(); diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp index d8091f32..b0da8205 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/DerpVersionBuilder.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "DerpVersion.h" #include "DerpInstance.h" @@ -45,7 +46,7 @@ bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWi bool DerpVersionBuilder::build() { - clear(); + m_version->clear(); QDir root(m_instance->instanceRoot()); QDir patches(root.absoluteFilePath("patches/")); @@ -112,20 +113,6 @@ bool DerpVersionBuilder::build() return true; } -void DerpVersionBuilder::clear() -{ - m_version->id.clear(); - m_version->time.clear(); - m_version->releaseTime.clear(); - m_version->type.clear(); - m_version->assets.clear(); - m_version->processArguments.clear(); - m_version->minecraftArguments.clear(); - m_version->minimumLauncherVersion = 0xDEADBEAF; - m_version->mainClass.clear(); - m_version->libraries.clear(); -} - void applyString(const QJsonObject &obj, const QString &key, QString &out) { if (obj.contains(key) && obj.value(key).isString()) @@ -181,6 +168,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) } // libraries + if (object.contains("libraries")) { auto librariesValue = object.value("libraries"); if (!librariesValue.isArray()) diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 8a120eab..8aed47e7 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -15,12 +15,18 @@ #include "LiteLoaderInstaller.h" +#include +#include + +#include "logger/QsLog.h" + #include "DerpVersion.h" #include "DerpLibrary.h" +#include "DerpInstance.h" QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; -LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion) +LiteLoaderInstaller::LiteLoaderInstaller() { if (m_launcherWrapperVersionMapping.isEmpty()) { @@ -31,71 +37,82 @@ LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion } } -bool LiteLoaderInstaller::canApply() const +bool LiteLoaderInstaller::canApply(DerpInstance *instance) const { - return m_launcherWrapperVersionMapping.contains(m_mcVersion); + return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId()); } -bool LiteLoaderInstaller::apply(std::shared_ptr to) +bool LiteLoaderInstaller::isApplied(DerpInstance *on) { - // DERPFIX + return QFile::exists(filename(on->instanceRoot())); +} - applyLaunchwrapper(to); - applyLiteLoader(to); - - to->mainClass = "net.minecraft.launchwrapper.Launch"; - if (!to->minecraftArguments.contains( - " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")) +bool LiteLoaderInstaller::add(DerpInstance *to) +{ + if (!patchesDir(to->instanceRoot()).exists()) { - to->minecraftArguments.append( - " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"); + QDir(to->instanceRoot()).mkdir("patches"); } + if (isApplied(to)) + { + if (!remove(to)) + { + return false; + } + } + + QJsonObject obj; + + obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch")); + obj.insert("+minecraftArguments", QString(" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")); + obj.insert("order", 10); + + QJsonArray libraries; + + // launchwrapper + { + DerpLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); + launchwrapperLib.finalize(); + QJsonObject lwLibObj = launchwrapperLib.toJson(); + lwLibObj.insert("insert", QString("beginning")); + libraries.append(lwLibObj); + } + + // liteloader + { + DerpLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId()); + liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/"); + liteloaderLib.finalize(); + QJsonObject llLibObj = liteloaderLib.toJson(); + llLibObj.insert("insert", QString("beginning")); + libraries.append(llLibObj); + } + + obj.insert("+libraries", libraries); + + QFile file(filename(to->instanceRoot())); + if (!file.open(QFile::WriteOnly)) + { + QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + return false; + } + file.write(QJsonDocument(obj).toJson()); + file.close(); + return true; } -void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr to) +bool LiteLoaderInstaller::remove(DerpInstance *from) { - const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion]; - - QMutableListIterator> it(to->libraries); - while (it.hasNext()) - { - it.next(); - if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:")) - { - if (it.value()->version() >= intendedVersion) - { - return; - } - else - { - it.remove(); - } - } - } - - std::shared_ptr lib(new DerpLibrary( - "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion])); - lib->finalize(); - to->libraries.prepend(lib); + return QFile::remove(filename(from->instanceRoot())); } -void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr to) +QString LiteLoaderInstaller::filename(const QString &root) const { - QMutableListIterator> it(to->libraries); - while (it.hasNext()) - { - it.next(); - if (it.value()->rawName().startsWith("com.mumfrey:liteloader:")) - { - it.remove(); - } - } - - std::shared_ptr lib( - new DerpLibrary("com.mumfrey:liteloader:" + m_mcVersion)); - lib->setBaseUrl("http://dl.liteloader.com/versions/"); - lib->finalize(); - to->libraries.prepend(lib); + return patchesDir(root).absoluteFilePath(id() + ".json"); +} +QDir LiteLoaderInstaller::patchesDir(const QString &root) const +{ + return QDir(root + "/patches/"); } diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index d7b8ce77..48ef4baf 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -19,21 +19,25 @@ #include class DerpVersion; +class DerpInstance; +class QDir; +// TODO base class class LiteLoaderInstaller { public: - LiteLoaderInstaller(const QString &mcVersion); + LiteLoaderInstaller(); - bool canApply() const; + bool canApply(DerpInstance *instance) const; + bool isApplied(DerpInstance *on); - bool apply(std::shared_ptr to); + bool add(DerpInstance *to); + bool remove(DerpInstance *from); private: - QString m_mcVersion; - - void applyLaunchwrapper(std::shared_ptr to); - void applyLiteLoader(std::shared_ptr to); + virtual QString id() const { return "com.mumfrey.liteloader"; } + QString filename(const QString &root) const; + QDir patchesDir(const QString &root) const; static QMap m_launcherWrapperVersionMapping; }; From c39d26f4453166603749826eee1a732c815047d0 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Wed, 22 Jan 2014 22:15:50 +0100 Subject: [PATCH 16/49] Got liteloader working. Patching more or less works --- logic/DerpRule.cpp | 2 +- logic/DerpRule.h | 2 +- logic/DerpVersionBuilder.cpp | 272 ++++++++++++++++++++++++++-------- logic/DerpVersionBuilder.h | 8 + logic/LiteLoaderInstaller.cpp | 4 +- 5 files changed, 221 insertions(+), 67 deletions(-) diff --git a/logic/DerpRule.cpp b/logic/DerpRule.cpp index d4cf1ba3..5ed200e3 100644 --- a/logic/DerpRule.cpp +++ b/logic/DerpRule.cpp @@ -18,7 +18,7 @@ #include "DerpRule.h" -QList> rulesFromJsonV4(QJsonObject &objectWithRules) +QList> rulesFromJsonV4(const QJsonObject &objectWithRules) { QList> rules; auto rulesVal = objectWithRules.value("rules"); diff --git a/logic/DerpRule.h b/logic/DerpRule.h index 7895ea98..0254028c 100644 --- a/logic/DerpRule.h +++ b/logic/DerpRule.h @@ -27,7 +27,7 @@ enum RuleAction }; RuleAction RuleAction_fromString(QString); -QList> rulesFromJsonV4(QJsonObject &objectWithRules); +QList> rulesFromJsonV4(const QJsonObject &objectWithRules); class Rule { diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp index b0da8205..eb215cca 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/DerpVersionBuilder.cpp @@ -113,12 +113,23 @@ bool DerpVersionBuilder::build() return true; } -void applyString(const QJsonObject &obj, const QString &key, QString &out) +void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) { if (obj.contains(key) && obj.value(key).isString()) { out = obj.value(key).toString(); } + else if (!onlyOverride) + { + if (obj.contains("+" + key) && obj.value("+" + key).isString()) + { + out += obj.value("+" + key).toString(); + } + else if (obj.contains("-" + key) && obj.value("-" + key).isString()) + { + out.remove(obj.value("-" + key).toString()); + } + } } void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(DerpLibrary::*func)(const QString &val)) { @@ -131,7 +142,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) { applyString(object, "id", m_version->id); applyString(object, "mainClass", m_version->mainClass); - applyString(object, "processArguments", m_version->processArguments); + applyString(object, "processArguments", m_version->processArguments, false); { const QString toCompare = m_version->processArguments.toLower(); if (toCompare == "legacy") @@ -147,7 +158,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; } } - applyString(object, "minecraftArguments", m_version->minecraftArguments); + applyString(object, "minecraftArguments", m_version->minecraftArguments, false); applyString(object, "type", m_version->type); applyString(object, "releaseTime", m_version->releaseTime); applyString(object, "time", m_version->time); @@ -170,80 +181,215 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) // libraries if (object.contains("libraries")) { + m_version->libraries.clear(); auto librariesValue = object.value("libraries"); if (!librariesValue.isArray()) { QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); return false; } + auto array = librariesValue.toArray(); + for (auto libVal : array) + { + if (libVal.isObject()) + { + if (!applyLibrary(libVal.toObject(), Override)) + { + return false; + } + } + + } + } + + // +libraries + if (object.contains("+libraries")) + { + auto librariesValue = object.value("+libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } for (auto libVal : librariesValue.toArray()) { - if (!libVal.isObject()) + if (libVal.isObject()) { - continue; + applyLibrary(libVal.toObject(), Add); } - QJsonObject libObj = libVal.toObject(); - - // Library name - auto nameVal = libObj.value("name"); - if (!nameVal.isString()) - { - continue; - } - std::shared_ptr library(new DerpLibrary(nameVal.toString())); - - applyString(libObj, "url", library, &DerpLibrary::setBaseUrl); - applyString(libObj, "MMC-hint", library, &DerpLibrary::setHint); - applyString(libObj, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); - applyString(libObj, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); - - auto extractVal = libObj.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) - { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) - { - if (excludeVal.isString()) - { - excludes.append(excludeVal.toString()); - } - } - library->extract_excludes = excludes; - } - } - - auto nativesVal = libObj.value("natives"); - if (nativesVal.isObject()) - { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - auto osType = OpSys_fromString(it.key()); - if (osType == Os_Other) - { - continue; - } - if (!it.value().isString()) - { - continue; - } - library->addNative(osType, it.value().toString()); - } - } - - library->setRules(rulesFromJsonV4(libObj)); - library->finalize(); - m_version->libraries.append(library); } } + // -libraries + if (object.contains("-libraries")) + { + auto librariesValue = object.value("-libraries"); + if (!librariesValue.isArray()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); + return false; + } + for (auto libVal : librariesValue.toArray()) + { + if (libVal.isObject()) + { + applyLibrary(libVal.toObject(), Remove); + } + + } + } + + return true; +} + +int findLibrary(QList > haystack, const QString &needle) +{ + for (int i = 0; i < haystack.size(); ++i) + { + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1) + { + return i; + } + } + return -1; +} + +bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionBuilder::Type type) +{ + // Library name + auto nameVal = lib.value("name"); + if (!nameVal.isString()) + { + return false; + } + auto name = nameVal.toString(); + + if (type == Remove) + { + int index = findLibrary(m_version->libraries, name); + if (index >= 0) + { + m_version->libraries.removeAt(index); + } + return true; + } + + if (type == Add && !lib.contains("insert")) + { + return false; + } + + std::shared_ptr library; + + if (lib.value("insert").toString() == "apply" && type == Add) + { + library = m_version->libraries[findLibrary(m_version->libraries, name)]; + } + else + { + library.reset(new DerpLibrary(nameVal.toString())); + } + + applyString(lib, "url", library, &DerpLibrary::setBaseUrl); + applyString(lib, "MMC-hint", library, &DerpLibrary::setHint); + applyString(lib, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); + applyString(lib, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); + + auto extractVal = lib.value("extract"); + if (extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if (excludesVal.isArray()) + { + auto excludesList = excludesVal.toArray(); + for (auto excludeVal : excludesList) + { + if (excludeVal.isString()) + { + excludes.append(excludeVal.toString()); + } + } + library->extract_excludes = excludes; + } + } + + auto nativesVal = lib.value("natives"); + if (nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + auto osType = OpSys_fromString(it.key()); + if (osType == Os_Other) + { + continue; + } + if (!it.value().isString()) + { + continue; + } + library->addNative(osType, it.value().toString()); + } + } + + if (lib.contains("rules")) + { + library->setRules(rulesFromJsonV4(lib)); + } + library->finalize(); + if (type == Override) + { + qDebug() << "appending" << library->rawName(); + m_version->libraries.append(library); + } + else if (lib.value("insert").toString() != "apply") + { + if (lib.value("insert").toString() == "append") + { + m_version->libraries.append(library); + } + else if (lib.value("insert").toString() == "prepend") + { + m_version->libraries.prepend(library); + } + else if (lib.value("insert").isObject()) + { + QJsonObject insertObj = lib.value("insert").toObject(); + if (insertObj.isEmpty()) + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); + return false; + } + const QString key = insertObj.keys().first(); + const QString value = insertObj.value(key).toString(); + const int index = findLibrary(m_version->libraries, value); + if (index >= 0) + { + if (key == "before") + { + m_version->libraries.insert(index, library); + } + else if (key == "after") + { + m_version->libraries.insert(index + 1, library); + } + else + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); + return false; + } + } + } + else + { + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); + return false; + } + } return true; } diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h index 739065db..5354cb0e 100644 --- a/logic/DerpVersionBuilder.h +++ b/logic/DerpVersionBuilder.h @@ -34,10 +34,18 @@ private: DerpInstance *m_instance; QWidget *m_widgetParent; + enum Type + { + Override, + Add, + Remove + }; + bool build(); void clear(); bool apply(const QJsonObject &object); + bool applyLibrary(const QJsonObject &lib, const Type type); bool read(const QFileInfo &fileInfo, QJsonObject *out); }; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 8aed47e7..d582f03a 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -75,7 +75,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) DerpLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); - lwLibObj.insert("insert", QString("beginning")); + lwLibObj.insert("insert", QString("prepend")); libraries.append(lwLibObj); } @@ -85,7 +85,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/"); liteloaderLib.finalize(); QJsonObject llLibObj = liteloaderLib.toJson(); - llLibObj.insert("insert", QString("beginning")); + llLibObj.insert("insert", QString("prepend")); libraries.append(llLibObj); } From 156bc8f27656c115bf1b023cd4ebc5f629df3887 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Thu, 23 Jan 2014 21:31:41 +0100 Subject: [PATCH 17/49] Forge works now too, and so does forge+liteloader --- CMakeLists.txt | 2 + gui/dialogs/DerpModEditDialog.cpp | 16 +++++--- logic/BaseInstaller.cpp | 66 +++++++++++++++++++++++++++++++ logic/BaseInstaller.h | 39 ++++++++++++++++++ logic/DerpFTBInstance.cpp | 2 +- logic/DerpVersion.cpp | 47 ++++++++++++++-------- logic/DerpVersion.h | 4 ++ logic/DerpVersionBuilder.cpp | 37 ++++++++++++++++- logic/DerpVersionBuilder.h | 2 + logic/ForgeInstaller.cpp | 48 ++++++++++++++++------ logic/ForgeInstaller.h | 9 ++++- logic/LiteLoaderInstaller.cpp | 32 ++------------- logic/LiteLoaderInstaller.h | 22 ++++------- 13 files changed, 245 insertions(+), 81 deletions(-) create mode 100644 logic/BaseInstaller.cpp create mode 100644 logic/BaseInstaller.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 43de626b..c5339a30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,6 +416,8 @@ logic/DerpRule.h logic/DerpRule.cpp logic/OpSys.h logic/OpSys.cpp +logic/BaseInstaller.h +logic/BaseInstaller.cpp logic/ForgeInstaller.h logic/ForgeInstaller.cpp logic/LiteLoaderInstaller.h diff --git a/gui/dialogs/DerpModEditDialog.cpp b/gui/dialogs/DerpModEditDialog.cpp index aafaa2e2..216aa59d 100644 --- a/gui/dialogs/DerpModEditDialog.cpp +++ b/gui/dialogs/DerpModEditDialog.cpp @@ -55,7 +55,6 @@ DerpModEditDialog::DerpModEditDialog(DerpInstance *inst, QWidget *parent) main_model->setSourceModel(m_version.get()); ui->libraryTreeView->setModel(main_model); ui->libraryTreeView->installEventFilter(this); - ui->mainClassEdit->setText(m_version->mainClass); updateVersionControls(); } else @@ -98,6 +97,7 @@ void DerpModEditDialog::updateVersionControls() ui->forgeBtn->setEnabled(true); ui->liteloaderBtn->setEnabled(LiteLoaderInstaller().canApply(m_inst)); ui->customEditorBtn->setEnabled(customVersion); + ui->mainClassEdit->setText(m_version->mainClass); } void DerpModEditDialog::disableVersionControls() @@ -105,6 +105,7 @@ void DerpModEditDialog::disableVersionControls() ui->forgeBtn->setEnabled(false); ui->liteloaderBtn->setEnabled(false); ui->customEditorBtn->setEnabled(false); + ui->mainClassEdit->setText(""); } void DerpModEditDialog::on_customEditorBtn_clicked() @@ -140,9 +141,9 @@ void DerpModEditDialog::on_forgeBtn_clicked() // install QString forgePath = entry->getFullPath(); ForgeInstaller forge(forgePath, forgeVersion->universal_url); - if (!forge.apply(m_version)) + if (!forge.add(m_inst)) { - // failure notice + QLOG_ERROR() << "Failure installing forge"; } } else @@ -155,12 +156,13 @@ void DerpModEditDialog::on_forgeBtn_clicked() // install QString forgePath = entry->getFullPath(); ForgeInstaller forge(forgePath, forgeVersion->universal_url); - if (!forge.apply(m_version)) + if (!forge.add(m_inst)) { - // failure notice + QLOG_ERROR() << "Failure installing forge"; } } } + m_inst->reloadFullVersion(this); } void DerpModEditDialog::on_liteloaderBtn_clicked() @@ -180,6 +182,10 @@ void DerpModEditDialog::on_liteloaderBtn_clicked() this, tr("LiteLoader"), tr("For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details.")); } + else + { + m_inst->reloadFullVersion(this); + } } bool DerpModEditDialog::loaderListFilter(QKeyEvent *keyEvent) diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp new file mode 100644 index 00000000..74a8e909 --- /dev/null +++ b/logic/BaseInstaller.cpp @@ -0,0 +1,66 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 "BaseInstaller.h" + +#include + +#include "DerpVersion.h" +#include "DerpLibrary.h" +#include "DerpInstance.h" + +#include "cmdutils.h" + +BaseInstaller::BaseInstaller() +{ + +} + +bool BaseInstaller::isApplied(DerpInstance *on) +{ + return QFile::exists(filename(on->instanceRoot())); +} + +bool BaseInstaller::add(DerpInstance *to) +{ + if (!patchesDir(to->instanceRoot()).exists()) + { + QDir(to->instanceRoot()).mkdir("patches"); + } + + if (isApplied(to)) + { + if (!remove(to)) + { + return false; + } + } + + return true; +} + +bool BaseInstaller::remove(DerpInstance *from) +{ + return QFile::remove(filename(from->instanceRoot())); +} + +QString BaseInstaller::filename(const QString &root) const +{ + return patchesDir(root).absoluteFilePath(id() + ".json"); +} +QDir BaseInstaller::patchesDir(const QString &root) const +{ + return QDir(root + "/patches/"); +} diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h new file mode 100644 index 00000000..08f78bfc --- /dev/null +++ b/logic/BaseInstaller.h @@ -0,0 +1,39 @@ +/* Copyright 2013 MultiMC Contributors + * + * 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 + +class DerpInstance; +class QDir; +class QString; + +class BaseInstaller +{ +public: + BaseInstaller(); + + virtual bool canApply(DerpInstance *instance) const { return true; } + bool isApplied(DerpInstance *on); + + virtual bool add(DerpInstance *to); + virtual bool remove(DerpInstance *from); + +protected: + virtual QString id() const = 0; + QString filename(const QString &root) const; + QDir patchesDir(const QString &root) const; +}; diff --git a/logic/DerpFTBInstance.cpp b/logic/DerpFTBInstance.cpp index f9aeeca0..9b687461 100644 --- a/logic/DerpFTBInstance.cpp +++ b/logic/DerpFTBInstance.cpp @@ -61,7 +61,7 @@ slots: return; } auto version = instance->getFullVersion(); - if (!forge.apply(version)) + if (!forge.add(instance)) { emitFailed(tr("Couldn't install Forge")); return; diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp index cabb2b24..f4a8dd97 100644 --- a/logic/DerpVersion.cpp +++ b/logic/DerpVersion.cpp @@ -44,6 +44,26 @@ void DerpVersion::clear() libraries.clear(); } +void DerpVersion::dump() const +{ + qDebug().nospace() << "DerpVersion(" + << "\n\tid=" << id + << "\n\ttime=" << time + << "\n\treleaseTime=" << releaseTime + << "\n\ttype=" << type + << "\n\tassets=" << assets + << "\n\tprocessArguments=" << processArguments + << "\n\tminecraftArguments=" << minecraftArguments + << "\n\tminimumLauncherVersion=" << minimumLauncherVersion + << "\n\tmainClass=" << mainClass + << "\n\tlibraries="; + for (auto lib : libraries) + { + qDebug().nospace() << "\n\t\t" << lib.get(); + } + qDebug().nospace() << "\n)"; +} + QList > DerpVersion::getActiveNormalLibs() { QList > output; @@ -70,6 +90,16 @@ QList > DerpVersion::getActiveNativeLibs() return output; } +std::shared_ptr DerpVersion::fromJson(const QJsonObject &obj) +{ + std::shared_ptr version(new DerpVersion(0)); + if (DerpVersionBuilder::read(version.get(), obj)) + { + return version; + } + return 0; +} + QVariant DerpVersion::data(const QModelIndex &index, int role) const { if (!index.isValid()) @@ -143,22 +173,7 @@ int DerpVersion::columnCount(const QModelIndex &parent) const QDebug operator<<(QDebug &dbg, const DerpVersion *version) { - dbg.nospace() << "DerpVersion(" - << "\n\tid=" << version->id - << "\n\ttime=" << version->time - << "\n\treleaseTime=" << version->releaseTime - << "\n\ttype=" << version->type - << "\n\tassets=" << version->assets - << "\n\tprocessArguments=" << version->processArguments - << "\n\tminecraftArguments=" << version->minecraftArguments - << "\n\tminimumLauncherVersion=" << version->minimumLauncherVersion - << "\n\tmainClass=" << version->mainClass - << "\n\tlibraries="; - for (auto lib : version->libraries) - { - dbg.nospace() << "\n\t\t" << lib.get(); - } - dbg.nospace() << "\n)"; + version->dump(); return dbg.maybeSpace(); } QDebug operator<<(QDebug &dbg, const DerpLibrary *library) diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h index f2132ad8..4481d146 100644 --- a/logic/DerpVersion.h +++ b/logic/DerpVersion.h @@ -41,10 +41,14 @@ public: bool reload(QWidget *widgetParent); void clear(); + void dump() const; + public: QList> getActiveNormalLibs(); QList> getActiveNativeLibs(); + static std::shared_ptr fromJson(const QJsonObject &obj); + // data members public: /// the ID - determines which jar to use! ACTUALLY IMPORTANT! diff --git a/logic/DerpVersionBuilder.cpp b/logic/DerpVersionBuilder.cpp index eb215cca..20f43404 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/DerpVersionBuilder.cpp @@ -29,6 +29,7 @@ #include "DerpVersion.h" #include "DerpInstance.h" #include "DerpRule.h" +#include "logger/QsLog.h" DerpVersionBuilder::DerpVersionBuilder() { @@ -44,6 +45,15 @@ bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWi return builder.build(); } +bool DerpVersionBuilder::read(DerpVersion *version, const QJsonObject &obj) +{ + DerpVersionBuilder builder; + builder.m_version = version; + builder.m_instance = 0; + builder.m_widgetParent = 0; + return builder.read(obj); +} + bool DerpVersionBuilder::build() { m_version->clear(); @@ -55,6 +65,7 @@ bool DerpVersionBuilder::build() // version.json { + QLOG_INFO() << "Reading version.json"; QJsonObject obj; if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) { @@ -73,6 +84,7 @@ bool DerpVersionBuilder::build() QMap objects; for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { + QLOG_INFO() << "Reading" << info.fileName(); QJsonObject obj; if (!read(info, &obj)) { @@ -87,6 +99,7 @@ bool DerpVersionBuilder::build() } for (auto object : objects.values()) { + qDebug() << "Applying object with order" << objects.key(object); if (!apply(object)) { return false; @@ -98,6 +111,7 @@ bool DerpVersionBuilder::build() { if (QFile::exists(root.absoluteFilePath("custom.json"))) { + QLOG_INFO() << "Reading custom.json"; QJsonObject obj; if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) { @@ -113,6 +127,13 @@ bool DerpVersionBuilder::build() return true; } +bool DerpVersionBuilder::read(const QJsonObject &obj) +{ + m_version->clear(); + + return apply(obj); +} + void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) { if (obj.contains(key) && obj.value(key).isString()) @@ -142,7 +163,9 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) { applyString(object, "id", m_version->id); applyString(object, "mainClass", m_version->mainClass); + applyString(object, "minecraftArguments", m_version->minecraftArguments, false); applyString(object, "processArguments", m_version->processArguments, false); + if (m_version->minecraftArguments.isEmpty()) { const QString toCompare = m_version->processArguments.toLower(); if (toCompare == "legacy") @@ -158,7 +181,6 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; } } - applyString(object, "minecraftArguments", m_version->minecraftArguments, false); applyString(object, "type", m_version->type); applyString(object, "releaseTime", m_version->releaseTime); applyString(object, "time", m_version->time); @@ -282,6 +304,18 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB std::shared_ptr library; + if (lib.value("insert").toString() != "apply" && type == Add) + { + QMutableListIterator > it(m_version->libraries); + while (it.hasNext()) + { + if (it.next()->rawName() == name) + { + it.remove(); + } + } + } + if (lib.value("insert").toString() == "apply" && type == Add) { library = m_version->libraries[findLibrary(m_version->libraries, name)]; @@ -343,7 +377,6 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB library->finalize(); if (type == Override) { - qDebug() << "appending" << library->rawName(); m_version->libraries.append(library); } else if (lib.value("insert").toString() != "apply") diff --git a/logic/DerpVersionBuilder.h b/logic/DerpVersionBuilder.h index 5354cb0e..b94cd8e9 100644 --- a/logic/DerpVersionBuilder.h +++ b/logic/DerpVersionBuilder.h @@ -28,6 +28,7 @@ class DerpVersionBuilder DerpVersionBuilder(); public: static bool build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent); + static bool read(DerpVersion *version, const QJsonObject &obj); private: DerpVersion *m_version; @@ -42,6 +43,7 @@ private: }; bool build(); + bool read(const QJsonObject &obj); void clear(); bool apply(const QJsonObject &object); diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 29735a7d..35663ddd 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -22,13 +22,13 @@ #include #include #include "MultiMC.h" +#include "DerpInstance.h" #include +#include #include #include -// DERPFIX - ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { std::shared_ptr newVersion; @@ -64,7 +64,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - // DERPFIX newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); + newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } @@ -109,12 +109,22 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::apply(std::shared_ptr to) +bool ForgeInstaller::add(DerpInstance *to) { + if (!BaseInstaller::add(to)) + { + return false; + } + + QJsonObject obj; + obj.insert("order", 5); + if (!m_forge_version) return false; int sliding_insert_window = 0; { + QJsonArray librariesPlus; + // for each library in the version we are adding (except for the blacklisted) QSet blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"}; for (auto lib : m_forge_version->libraries) @@ -133,27 +143,43 @@ bool ForgeInstaller::apply(std::shared_ptr to) if (blacklist.contains(libName)) continue; - // find an entry that matches this one + QJsonObject libObj = lib->toJson(); + bool found = false; - for (auto tolib : to->libraries) + // find an entry that matches this one + for (auto tolib : to->getFullVersion()->libraries) { if (tolib->name() != libName) continue; found = true; // replace lib - tolib = lib; + libObj.insert("insert", QString("apply")); break; } if (!found) { // add lib - to->libraries.insert(sliding_insert_window, lib); + QJsonObject insertObj; + insertObj.insert("before", to->getFullVersion()->libraries.at(sliding_insert_window)->rawName()); + libObj.insert("insert", insertObj); sliding_insert_window++; } + librariesPlus.append(libObj); } - to->mainClass = m_forge_version->mainClass; - to->minecraftArguments = m_forge_version->minecraftArguments; - to->processArguments = m_forge_version->processArguments; + obj.insert("+libraries", librariesPlus); + obj.insert("mainClass", m_forge_version->mainClass); + obj.insert("minecraftArguments", m_forge_version->minecraftArguments); + obj.insert("processArguments", m_forge_version->processArguments); } + + QFile file(filename(to->instanceRoot())); + if (!file.open(QFile::WriteOnly)) + { + QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + return false; + } + file.write(QJsonDocument(obj).toJson()); + file.close(); + return true; } diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index 65cad87c..6490b5bf 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -14,17 +14,22 @@ */ #pragma once + +#include "BaseInstaller.h" + #include #include class DerpVersion; -class ForgeInstaller +class ForgeInstaller : public BaseInstaller { public: ForgeInstaller(QString filename, QString universal_url); - bool apply(std::shared_ptr to); + bool add(DerpInstance *to) override; + + QString id() const override { return "net.minecraftforge"; } private: // the version, read from the installer diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index d582f03a..71c7011f 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -27,6 +27,7 @@ QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; LiteLoaderInstaller::LiteLoaderInstaller() + : BaseInstaller() { if (m_launcherWrapperVersionMapping.isEmpty()) { @@ -42,24 +43,11 @@ bool LiteLoaderInstaller::canApply(DerpInstance *instance) const return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId()); } -bool LiteLoaderInstaller::isApplied(DerpInstance *on) -{ - return QFile::exists(filename(on->instanceRoot())); -} - bool LiteLoaderInstaller::add(DerpInstance *to) { - if (!patchesDir(to->instanceRoot()).exists()) + if (!BaseInstaller::add(to)) { - QDir(to->instanceRoot()).mkdir("patches"); - } - - if (isApplied(to)) - { - if (!remove(to)) - { - return false; - } + return false; } QJsonObject obj; @@ -102,17 +90,3 @@ bool LiteLoaderInstaller::add(DerpInstance *to) return true; } - -bool LiteLoaderInstaller::remove(DerpInstance *from) -{ - return QFile::remove(filename(from->instanceRoot())); -} - -QString LiteLoaderInstaller::filename(const QString &root) const -{ - return patchesDir(root).absoluteFilePath(id() + ".json"); -} -QDir LiteLoaderInstaller::patchesDir(const QString &root) const -{ - return QDir(root + "/patches/"); -} diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index 48ef4baf..c128ee22 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -14,30 +14,22 @@ */ #pragma once + +#include "BaseInstaller.h" + #include #include -#include -class DerpVersion; -class DerpInstance; -class QDir; - -// TODO base class -class LiteLoaderInstaller +class LiteLoaderInstaller : public BaseInstaller { public: LiteLoaderInstaller(); - bool canApply(DerpInstance *instance) const; - bool isApplied(DerpInstance *on); - - bool add(DerpInstance *to); - bool remove(DerpInstance *from); + bool canApply(DerpInstance *instance) const override; + bool add(DerpInstance *to) override; private: - virtual QString id() const { return "com.mumfrey.liteloader"; } - QString filename(const QString &root) const; - QDir patchesDir(const QString &root) const; + virtual QString id() const override { return "com.mumfrey.liteloader"; } static QMap m_launcherWrapperVersionMapping; }; From 7d5787025aa5cebf6778f71e79f248f10b8541c9 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 24 Jan 2014 18:12:02 +0100 Subject: [PATCH 18/49] Change naming from Derp -> OneSix until the new instance type supports legacy --- CMakeLists.txt | 38 +-- gui/MainWindow.cpp | 4 +- ...EditDialog.cpp => OneSixModEditDialog.cpp} | 46 +-- ...pModEditDialog.h => OneSixModEditDialog.h} | 16 +- ...odEditDialog.ui => OneSixModEditDialog.ui} | 4 +- logic/BaseInstaller.cpp | 12 +- logic/BaseInstaller.h | 10 +- logic/BaseInstance.h | 2 +- logic/DerpVersion.cpp | 194 ------------ logic/DerpVersion.h | 115 ------- logic/ForgeInstaller.cpp | 14 +- logic/ForgeInstaller.h | 6 +- logic/InstanceFactory.cpp | 32 +- logic/LegacyInstance.h | 2 +- logic/LiteLoaderInstaller.cpp | 14 +- logic/LiteLoaderInstaller.h | 4 +- logic/MinecraftVersion.h | 6 +- logic/NostalgiaInstance.cpp | 2 +- logic/NostalgiaInstance.h | 4 +- ...pFTBInstance.cpp => OneSixFTBInstance.cpp} | 38 +-- ...{DerpFTBInstance.h => OneSixFTBInstance.h} | 10 +- .../{DerpInstance.cpp => OneSixInstance.cpp} | 86 +++--- logic/{DerpInstance.h => OneSixInstance.h} | 10 +- .../{DerpInstance_p.h => OneSixInstance_p.h} | 6 +- logic/{DerpLibrary.cpp => OneSixLibrary.cpp} | 38 +-- logic/{DerpLibrary.h => OneSixLibrary.h} | 4 +- logic/{DerpRule.cpp => OneSixRule.cpp} | 2 +- logic/{DerpRule.h => OneSixRule.h} | 10 +- logic/{DerpUpdate.cpp => OneSixUpdate.cpp} | 58 ++-- logic/{DerpUpdate.h => OneSixUpdate.h} | 4 +- logic/OneSixVersion.cpp | 292 +++++------------- logic/OneSixVersion.h | 45 +-- ...onBuilder.cpp => OneSixVersionBuilder.cpp} | 46 +-- ...ersionBuilder.h => OneSixVersionBuilder.h} | 16 +- logic/lists/MinecraftVersionList.cpp | 6 +- 35 files changed, 375 insertions(+), 821 deletions(-) rename gui/dialogs/{DerpModEditDialog.cpp => OneSixModEditDialog.cpp} (84%) rename gui/dialogs/{DerpModEditDialog.h => OneSixModEditDialog.h} (83%) rename gui/dialogs/{DerpModEditDialog.ui => OneSixModEditDialog.ui} (99%) delete mode 100644 logic/DerpVersion.cpp delete mode 100644 logic/DerpVersion.h rename logic/{DerpFTBInstance.cpp => OneSixFTBInstance.cpp} (69%) rename logic/{DerpFTBInstance.h => OneSixFTBInstance.h} (56%) rename logic/{DerpInstance.cpp => OneSixInstance.cpp} (80%) rename logic/{DerpInstance.h => OneSixInstance.h} (89%) rename logic/{DerpInstance_p.h => OneSixInstance_p.h} (85%) rename logic/{DerpLibrary.cpp => OneSixLibrary.cpp} (86%) rename logic/{DerpLibrary.h => OneSixLibrary.h} (98%) rename logic/{DerpRule.cpp => OneSixRule.cpp} (98%) rename logic/{DerpRule.h => OneSixRule.h} (90%) rename logic/{DerpUpdate.cpp => OneSixUpdate.cpp} (88%) rename logic/{DerpUpdate.h => OneSixUpdate.h} (92%) rename logic/{DerpVersionBuilder.cpp => OneSixVersionBuilder.cpp} (87%) rename logic/{DerpVersionBuilder.h => OneSixVersionBuilder.h} (76%) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5339a30..6218ee7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,8 +297,8 @@ gui/dialogs/IconPickerDialog.h gui/dialogs/IconPickerDialog.cpp gui/dialogs/LegacyModEditDialog.h gui/dialogs/LegacyModEditDialog.cpp -gui/dialogs/DerpModEditDialog.h -gui/dialogs/DerpModEditDialog.cpp +gui/dialogs/OneSixModEditDialog.h +gui/dialogs/OneSixModEditDialog.cpp gui/dialogs/ModEditDialogCommon.h gui/dialogs/ModEditDialogCommon.cpp gui/dialogs/EditNotesDialog.h @@ -405,15 +405,15 @@ logic/LegacyUpdate.cpp logic/LegacyForge.h logic/LegacyForge.cpp -# Derp instances -logic/DerpUpdate.h -logic/DerpUpdate.cpp -logic/DerpVersion.h -logic/DerpVersion.cpp -logic/DerpLibrary.h -logic/DerpLibrary.cpp -logic/DerpRule.h -logic/DerpRule.cpp +# OneSix instances +logic/OneSixUpdate.h +logic/OneSixUpdate.cpp +logic/OneSixVersion.h +logic/OneSixVersion.cpp +logic/OneSixLibrary.h +logic/OneSixLibrary.cpp +logic/OneSixRule.h +logic/OneSixRule.cpp logic/OpSys.h logic/OpSys.cpp logic/BaseInstaller.h @@ -422,19 +422,19 @@ logic/ForgeInstaller.h logic/ForgeInstaller.cpp logic/LiteLoaderInstaller.h logic/LiteLoaderInstaller.cpp -logic/DerpInstance.h -logic/DerpInstance.cpp -logic/DerpInstance_p.h -logic/DerpVersionBuilder.h -logic/DerpVersionBuilder.cpp +logic/OneSixInstance.h +logic/OneSixInstance.cpp +logic/OneSixInstance_p.h +logic/OneSixVersionBuilder.h +logic/OneSixVersionBuilder.cpp # Nostalgia logic/NostalgiaInstance.h logic/NostalgiaInstance.cpp # FTB -logic/DerpFTBInstance.h -logic/DerpFTBInstance.cpp +logic/OneSixFTBInstance.h +logic/OneSixFTBInstance.cpp logic/LegacyFTBInstance.h logic/LegacyFTBInstance.cpp @@ -510,7 +510,7 @@ gui/dialogs/InstanceSettings.ui gui/dialogs/ProgressDialog.ui gui/dialogs/IconPickerDialog.ui gui/dialogs/LegacyModEditDialog.ui -gui/dialogs/DerpModEditDialog.ui +gui/dialogs/OneSixModEditDialog.ui gui/dialogs/EditNotesDialog.ui gui/dialogs/AccountListDialog.ui gui/dialogs/AccountSelectDialog.ui diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index ba394a94..ee9c3fad 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -84,7 +84,7 @@ #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logic/MinecraftProcess.h" -#include "logic/DerpUpdate.h" +#include "logic/OneSixUpdate.h" #include "logic/JavaUtils.h" #include "logic/NagUtils.h" #include "logic/SkinUtils.h" @@ -1257,7 +1257,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() VersionSelectDialog vselect(m_selectedInstance->versionList().get(), tr("Change Minecraft version"), this); - vselect.setFilter(1, "Derp"); + vselect.setFilter(1, "OneSix"); if(!vselect.exec() || !vselect.selectedVersion()) return; diff --git a/gui/dialogs/DerpModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp similarity index 84% rename from gui/dialogs/DerpModEditDialog.cpp rename to gui/dialogs/OneSixModEditDialog.cpp index 216aa59d..625a7c91 100644 --- a/gui/dialogs/DerpModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -23,9 +23,9 @@ #include #include -#include "DerpModEditDialog.h" +#include "OneSixModEditDialog.h" #include "ModEditDialogCommon.h" -#include "ui_DerpModEditDialog.h" +#include "ui_OneSixModEditDialog.h" #include "gui/Platform.h" #include "gui/dialogs/CustomMessageBox.h" @@ -34,14 +34,14 @@ #include "gui/dialogs/ProgressDialog.h" #include "logic/ModList.h" -#include "logic/DerpVersion.h" +#include "logic/OneSixVersion.h" #include "logic/EnabledItemFilter.h" #include "logic/lists/ForgeVersionList.h" #include "logic/ForgeInstaller.h" #include "logic/LiteLoaderInstaller.h" -DerpModEditDialog::DerpModEditDialog(DerpInstance *inst, QWidget *parent) - : QDialog(parent), ui(new Ui::DerpModEditDialog), m_inst(inst) +OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) + : QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst) { MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); @@ -81,17 +81,17 @@ DerpModEditDialog::DerpModEditDialog(DerpInstance *inst, QWidget *parent) m_resourcepacks->startWatching(); } - connect(m_inst, &DerpInstance::versionReloaded, this, &DerpModEditDialog::updateVersionControls); + connect(m_inst, &OneSixInstance::versionReloaded, this, &OneSixModEditDialog::updateVersionControls); } -DerpModEditDialog::~DerpModEditDialog() +OneSixModEditDialog::~OneSixModEditDialog() { m_mods->stopWatching(); m_resourcepacks->stopWatching(); delete ui; } -void DerpModEditDialog::updateVersionControls() +void OneSixModEditDialog::updateVersionControls() { bool customVersion = m_inst->versionIsCustom(); ui->forgeBtn->setEnabled(true); @@ -100,7 +100,7 @@ void DerpModEditDialog::updateVersionControls() ui->mainClassEdit->setText(m_version->mainClass); } -void DerpModEditDialog::disableVersionControls() +void OneSixModEditDialog::disableVersionControls() { ui->forgeBtn->setEnabled(false); ui->liteloaderBtn->setEnabled(false); @@ -108,7 +108,7 @@ void DerpModEditDialog::disableVersionControls() ui->mainClassEdit->setText(""); } -void DerpModEditDialog::on_customEditorBtn_clicked() +void OneSixModEditDialog::on_customEditorBtn_clicked() { if (QDir(m_inst->instanceRoot()).exists("custom.json")) { @@ -119,7 +119,7 @@ void DerpModEditDialog::on_customEditorBtn_clicked() } } -void DerpModEditDialog::on_forgeBtn_clicked() +void OneSixModEditDialog::on_forgeBtn_clicked() { VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); vselect.setFilter(1, m_inst->currentVersionId()); @@ -165,7 +165,7 @@ void DerpModEditDialog::on_forgeBtn_clicked() m_inst->reloadFullVersion(this); } -void DerpModEditDialog::on_liteloaderBtn_clicked() +void OneSixModEditDialog::on_liteloaderBtn_clicked() { LiteLoaderInstaller liteloader; if (!liteloader.canApply(m_inst)) @@ -188,7 +188,7 @@ void DerpModEditDialog::on_liteloaderBtn_clicked() } } -bool DerpModEditDialog::loaderListFilter(QKeyEvent *keyEvent) +bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent) { switch (keyEvent->key()) { @@ -204,7 +204,7 @@ bool DerpModEditDialog::loaderListFilter(QKeyEvent *keyEvent) return QDialog::eventFilter(ui->loaderModTreeView, keyEvent); } -bool DerpModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) +bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) { switch (keyEvent->key()) { @@ -220,7 +220,7 @@ bool DerpModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) return QDialog::eventFilter(ui->resPackTreeView, keyEvent); } -bool DerpModEditDialog::eventFilter(QObject *obj, QEvent *ev) +bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() != QEvent::KeyPress) { @@ -234,12 +234,12 @@ bool DerpModEditDialog::eventFilter(QObject *obj, QEvent *ev) return QDialog::eventFilter(obj, ev); } -void DerpModEditDialog::on_buttonBox_rejected() +void OneSixModEditDialog::on_buttonBox_rejected() { close(); } -void DerpModEditDialog::on_addModBtn_clicked() +void OneSixModEditDialog::on_addModBtn_clicked() { QStringList fileNames = QFileDialog::getOpenFileNames( this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods")); @@ -250,7 +250,7 @@ void DerpModEditDialog::on_addModBtn_clicked() m_mods->startWatching(); } } -void DerpModEditDialog::on_rmModBtn_clicked() +void OneSixModEditDialog::on_rmModBtn_clicked() { int first, last; auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); @@ -261,12 +261,12 @@ void DerpModEditDialog::on_rmModBtn_clicked() m_mods->deleteMods(first, last); m_mods->startWatching(); } -void DerpModEditDialog::on_viewModBtn_clicked() +void OneSixModEditDialog::on_viewModBtn_clicked() { openDirInDefaultProgram(m_inst->loaderModsDir(), true); } -void DerpModEditDialog::on_addResPackBtn_clicked() +void OneSixModEditDialog::on_addResPackBtn_clicked() { QStringList fileNames = QFileDialog::getOpenFileNames( this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs")); @@ -277,7 +277,7 @@ void DerpModEditDialog::on_addResPackBtn_clicked() m_resourcepacks->startWatching(); } } -void DerpModEditDialog::on_rmResPackBtn_clicked() +void OneSixModEditDialog::on_rmResPackBtn_clicked() { int first, last; auto list = ui->resPackTreeView->selectionModel()->selectedRows(); @@ -288,12 +288,12 @@ void DerpModEditDialog::on_rmResPackBtn_clicked() m_resourcepacks->deleteMods(first, last); m_resourcepacks->startWatching(); } -void DerpModEditDialog::on_viewResPackBtn_clicked() +void OneSixModEditDialog::on_viewResPackBtn_clicked() { openDirInDefaultProgram(m_inst->resourcePacksDir(), true); } -void DerpModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous) +void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previous) { if (!current.isValid()) { diff --git a/gui/dialogs/DerpModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h similarity index 83% rename from gui/dialogs/DerpModEditDialog.h rename to gui/dialogs/OneSixModEditDialog.h index e9e61c65..161604c9 100644 --- a/gui/dialogs/DerpModEditDialog.h +++ b/gui/dialogs/OneSixModEditDialog.h @@ -16,21 +16,21 @@ #pragma once #include -#include +#include class EnabledItemFilter; namespace Ui { -class DerpModEditDialog; +class OneSixModEditDialog; } -class DerpModEditDialog : public QDialog +class OneSixModEditDialog : public QDialog { Q_OBJECT public: - explicit DerpModEditDialog(DerpInstance *inst, QWidget *parent = 0); - virtual ~DerpModEditDialog(); + explicit OneSixModEditDialog(OneSixInstance *inst, QWidget *parent = 0); + virtual ~OneSixModEditDialog(); private slots: @@ -55,12 +55,12 @@ protected: bool resourcePackListFilter(QKeyEvent *ev); private: - Ui::DerpModEditDialog *ui; - std::shared_ptr m_version; + Ui::OneSixModEditDialog *ui; + std::shared_ptr m_version; std::shared_ptr m_mods; std::shared_ptr m_resourcepacks; EnabledItemFilter *main_model; - DerpInstance *m_inst; + OneSixInstance *m_inst; public slots: void loaderCurrent(QModelIndex current, QModelIndex previous); diff --git a/gui/dialogs/DerpModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui similarity index 99% rename from gui/dialogs/DerpModEditDialog.ui rename to gui/dialogs/OneSixModEditDialog.ui index 7aaf1564..d57d0f85 100644 --- a/gui/dialogs/DerpModEditDialog.ui +++ b/gui/dialogs/OneSixModEditDialog.ui @@ -1,7 +1,7 @@ - DerpModEditDialog - + OneSixModEditDialog + 0 diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp index 74a8e909..92aa0c92 100644 --- a/logic/BaseInstaller.cpp +++ b/logic/BaseInstaller.cpp @@ -17,9 +17,9 @@ #include -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "DerpInstance.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "OneSixInstance.h" #include "cmdutils.h" @@ -28,12 +28,12 @@ BaseInstaller::BaseInstaller() } -bool BaseInstaller::isApplied(DerpInstance *on) +bool BaseInstaller::isApplied(OneSixInstance *on) { return QFile::exists(filename(on->instanceRoot())); } -bool BaseInstaller::add(DerpInstance *to) +bool BaseInstaller::add(OneSixInstance *to) { if (!patchesDir(to->instanceRoot()).exists()) { @@ -51,7 +51,7 @@ bool BaseInstaller::add(DerpInstance *to) return true; } -bool BaseInstaller::remove(DerpInstance *from) +bool BaseInstaller::remove(OneSixInstance *from) { return QFile::remove(filename(from->instanceRoot())); } diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h index 08f78bfc..df7eab89 100644 --- a/logic/BaseInstaller.h +++ b/logic/BaseInstaller.h @@ -17,7 +17,7 @@ #include -class DerpInstance; +class OneSixInstance; class QDir; class QString; @@ -26,11 +26,11 @@ class BaseInstaller public: BaseInstaller(); - virtual bool canApply(DerpInstance *instance) const { return true; } - bool isApplied(DerpInstance *on); + virtual bool canApply(OneSixInstance *instance) const { return true; } + bool isApplied(OneSixInstance *on); - virtual bool add(DerpInstance *to); - virtual bool remove(DerpInstance *from); + virtual bool add(OneSixInstance *to); + virtual bool remove(OneSixInstance *from); protected: virtual QString id() const = 0; diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 79640c84..a861e9b2 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -27,7 +27,7 @@ class QDialog; class Task; class MinecraftProcess; -class DerpUpdate; +class OneSixUpdate; class InstanceList; class BaseInstancePrivate; diff --git a/logic/DerpVersion.cpp b/logic/DerpVersion.cpp deleted file mode 100644 index f4a8dd97..00000000 --- a/logic/DerpVersion.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * 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 "DerpVersion.h" - -#include - -#include "DerpVersionBuilder.h" - -DerpVersion::DerpVersion(DerpInstance *instance, QObject *parent) - : QAbstractListModel(parent), m_instance(instance) -{ - clear(); -} - -bool DerpVersion::reload(QWidget *widgetParent) -{ - return DerpVersionBuilder::build(this, m_instance, widgetParent); -} - -void DerpVersion::clear() -{ - id.clear(); - time.clear(); - releaseTime.clear(); - type.clear(); - assets.clear(); - processArguments.clear(); - minecraftArguments.clear(); - minimumLauncherVersion = 0xDEADBEAF; - mainClass.clear(); - libraries.clear(); -} - -void DerpVersion::dump() const -{ - qDebug().nospace() << "DerpVersion(" - << "\n\tid=" << id - << "\n\ttime=" << time - << "\n\treleaseTime=" << releaseTime - << "\n\ttype=" << type - << "\n\tassets=" << assets - << "\n\tprocessArguments=" << processArguments - << "\n\tminecraftArguments=" << minecraftArguments - << "\n\tminimumLauncherVersion=" << minimumLauncherVersion - << "\n\tmainClass=" << mainClass - << "\n\tlibraries="; - for (auto lib : libraries) - { - qDebug().nospace() << "\n\t\t" << lib.get(); - } - qDebug().nospace() << "\n)"; -} - -QList > DerpVersion::getActiveNormalLibs() -{ - QList > output; - for (auto lib : libraries) - { - if (lib->isActive() && !lib->isNative()) - { - output.append(lib); - } - } - return output; -} - -QList > DerpVersion::getActiveNativeLibs() -{ - QList > output; - for (auto lib : libraries) - { - if (lib->isActive() && lib->isNative()) - { - output.append(lib); - } - } - return output; -} - -std::shared_ptr DerpVersion::fromJson(const QJsonObject &obj) -{ - std::shared_ptr version(new DerpVersion(0)); - if (DerpVersionBuilder::read(version.get(), obj)) - { - return version; - } - return 0; -} - -QVariant DerpVersion::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - int row = index.row(); - int column = index.column(); - - if (row < 0 || row >= libraries.size()) - return QVariant(); - - if (role == Qt::DisplayRole) - { - switch (column) - { - case 0: - return libraries[row]->name(); - case 1: - return libraries[row]->type(); - case 2: - return libraries[row]->version(); - default: - return QVariant(); - } - } - return QVariant(); -} - -Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - int row = index.row(); - if (libraries[row]->isActive()) - { - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; - } - else - { - return Qt::ItemNeverHasChildren; - } - // return QAbstractListModel::flags(index); -} - -QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - switch (section) - { - case 0: - return QString("Name"); - case 1: - return QString("Type"); - case 2: - return QString("Version"); - default: - return QString(); - } -} - -int DerpVersion::rowCount(const QModelIndex &parent) const -{ - return libraries.size(); -} - -int DerpVersion::columnCount(const QModelIndex &parent) const -{ - return 3; -} - -QDebug operator<<(QDebug &dbg, const DerpVersion *version) -{ - version->dump(); - return dbg.maybeSpace(); -} -QDebug operator<<(QDebug &dbg, const DerpLibrary *library) -{ - dbg.nospace() << "DerpLibrary(" - << "\n\t\t\trawName=" << library->rawName() - << "\n\t\t\tname=" << library->name() - << "\n\t\t\tversion=" << library->version() - << "\n\t\t\ttype=" << library->type() - << "\n\t\t\tisActive=" << library->isActive() - << "\n\t\t\tisNative=" << library->isNative() - << "\n\t\t\tdownloadUrl=" << library->downloadUrl() - << "\n\t\t\tstoragePath=" << library->storagePath() - << "\n\t\t\tabsolutePath=" << library->absoluteUrl() - << "\n\t\t\thint=" << library->hint(); - dbg.nospace() << "\n\t\t)"; - return dbg.maybeSpace(); -} diff --git a/logic/DerpVersion.h b/logic/DerpVersion.h deleted file mode 100644 index 4481d146..00000000 --- a/logic/DerpVersion.h +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * 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 - -#include -#include -#include - -#include "DerpLibrary.h" - -class DerpInstance; - -class DerpVersion : public QAbstractListModel -{ - Q_OBJECT -public: - explicit DerpVersion(DerpInstance *instance, QObject *parent = 0); - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex &parent) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - bool reload(QWidget *widgetParent); - void clear(); - - void dump() const; - -public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); - - static std::shared_ptr fromJson(const QJsonObject &obj); - - // data members -public: - /// the ID - determines which jar to use! ACTUALLY IMPORTANT! - QString id; - /// Last updated time - as a string - QString time; - /// Release time - as a string - QString releaseTime; - /// Release type - "release" or "snapshot" - QString type; - /// Assets type - "legacy" or a version ID - QString assets; - /** - * DEPRECATED: Old versions of the new vanilla launcher used this - * ex: "username_session_version" - */ - QString processArguments; - /** - * arguments that should be used for launching minecraft - * - * ex: "--username ${auth_player_name} --session ${auth_session} - * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" - */ - QString minecraftArguments; - /** - * the minimum launcher version required by this version ... current is 4 (at point of - * writing) - */ - int minimumLauncherVersion = 0xDEADBEEF; - /** - * The main class to load first - */ - QString mainClass; - - /// the list of libs - both active and inactive, native and java - QList> libraries; - - /* - FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. - - "rules": [ - { - "action": "allow" - }, - { - "action": "disallow", - "os": { - "name": "osx", - "version": "^10\\.5\\.\\d$" - } - } - ], - "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX - 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" - } - */ - // QList rules; - -private: - DerpInstance *m_instance; -}; - -QDebug operator<<(QDebug &dbg, const DerpVersion *version); -QDebug operator<<(QDebug &dbg, const DerpLibrary *library); diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 35663ddd..863c3dfd 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -14,15 +14,15 @@ */ #include "ForgeInstaller.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "net/HttpMetaCache.h" #include #include #include #include #include "MultiMC.h" -#include "DerpInstance.h" +#include "OneSixInstance.h" #include #include @@ -31,7 +31,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - std::shared_ptr newVersion; + std::shared_ptr newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -64,7 +64,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - newVersion = DerpVersion::fromJson(versionInfoVal.toObject()); + newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } @@ -74,7 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) internalPath = installObj.value("filePath").toString(); // where do we put the library? decode the mojang path - DerpLibrary lib(libraryName); + OneSixLibrary lib(libraryName); lib.finalize(); auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath()); @@ -109,7 +109,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::add(DerpInstance *to) +bool ForgeInstaller::add(OneSixInstance *to) { if (!BaseInstaller::add(to)) { diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index 6490b5bf..eb8ecd79 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -20,20 +20,20 @@ #include #include -class DerpVersion; +class OneSixVersion; class ForgeInstaller : public BaseInstaller { public: ForgeInstaller(QString filename, QString universal_url); - bool add(DerpInstance *to) override; + bool add(OneSixInstance *to) override; QString id() const override { return "net.minecraftforge"; } private: // the version, read from the installer - std::shared_ptr m_forge_version; + std::shared_ptr m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index c50ec4e4..730bcd69 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -21,10 +21,10 @@ #include "BaseInstance.h" #include "LegacyInstance.h" #include "LegacyFTBInstance.h" -#include "DerpInstance.h" -#include "DerpFTBInstance.h" +#include "OneSixInstance.h" +#include "OneSixFTBInstance.h" #include "NostalgiaInstance.h" -#include "DerpInstance.h" +#include "OneSixInstance.h" #include "BaseVersion.h" #include "MinecraftVersion.h" @@ -51,9 +51,9 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst QString inst_type = m_settings->get("InstanceType").toString(); // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "Derp" || inst_type == "OneSix") + if (inst_type == "OneSix" || inst_type == "OneSix") { - inst = new DerpInstance(instDir, m_settings, this); + inst = new OneSixInstance(instDir, m_settings, this); } else if (inst_type == "Legacy") { @@ -67,9 +67,9 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new LegacyFTBInstance(instDir, m_settings, this); } - else if (inst_type == "OneSixFTB" || inst_type == "DerpFTB") + else if (inst_type == "OneSixFTB" || inst_type == "OneSixFTB") { - inst = new DerpFTBInstance(instDir, m_settings, this); + inst = new OneSixFTBInstance(instDir, m_settings, this); } else { @@ -102,15 +102,15 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& switch (mcVer->type) { case MinecraftVersion::Legacy: - // TODO derp + // TODO OneSix m_settings->set("InstanceType", "Legacy"); inst = new LegacyInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::Derp: - m_settings->set("InstanceType", "Derp"); - inst = new DerpInstance(instDir, m_settings, this); + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSix"); + inst = new OneSixInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -137,9 +137,9 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; - case MinecraftVersion::Derp: - m_settings->set("InstanceType", "DerpFTB"); - inst = new DerpFTBInstance(instDir, m_settings, this); + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSixFTB"); + inst = new OneSixFTBInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; @@ -176,8 +176,8 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); - if(inst_type == "OneSixFTB" || inst_type == "DerpFTB") - m_settings->set("InstanceType", "Derp"); + if(inst_type == "OneSixFTB" || inst_type == "OneSixFTB") + m_settings->set("InstanceType", "OneSix"); if(inst_type == "LegacyFTB") m_settings->set("InstanceType", "Legacy"); diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 1ff17cbd..1e7d9eb6 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -68,7 +68,7 @@ public: virtual QString intendedVersionId() const override; virtual bool setIntendedVersionId(QString version) override; // the `version' of Legacy instances is defined by the launcher code. - // in contrast with Derp, where `version' is described in a json file + // in contrast with OneSix, where `version' is described in a json file virtual bool versionIsCustom() override { return false; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 71c7011f..1ce9d3f4 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -20,9 +20,9 @@ #include "logger/QsLog.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "DerpInstance.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "OneSixInstance.h" QMap LiteLoaderInstaller::m_launcherWrapperVersionMapping; @@ -38,12 +38,12 @@ LiteLoaderInstaller::LiteLoaderInstaller() } } -bool LiteLoaderInstaller::canApply(DerpInstance *instance) const +bool LiteLoaderInstaller::canApply(OneSixInstance *instance) const { return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId()); } -bool LiteLoaderInstaller::add(DerpInstance *to) +bool LiteLoaderInstaller::add(OneSixInstance *to) { if (!BaseInstaller::add(to)) { @@ -60,7 +60,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) // launchwrapper { - DerpLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); + OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); lwLibObj.insert("insert", QString("prepend")); @@ -69,7 +69,7 @@ bool LiteLoaderInstaller::add(DerpInstance *to) // liteloader { - DerpLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId()); + OneSixLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId()); liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/"); liteloaderLib.finalize(); QJsonObject llLibObj = liteloaderLib.toJson(); diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h index c128ee22..5e01b16c 100644 --- a/logic/LiteLoaderInstaller.h +++ b/logic/LiteLoaderInstaller.h @@ -25,8 +25,8 @@ class LiteLoaderInstaller : public BaseInstaller public: LiteLoaderInstaller(); - bool canApply(DerpInstance *instance) const override; - bool add(DerpInstance *to) override; + bool canApply(OneSixInstance *instance) const override; + bool add(OneSixInstance *to) override; private: virtual QString id() const override { return "com.mumfrey.liteloader"; } diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h index dd849ce9..504381a8 100644 --- a/logic/MinecraftVersion.h +++ b/logic/MinecraftVersion.h @@ -32,7 +32,7 @@ struct MinecraftVersion : public BaseVersion /// This version's type. Used internally to identify what kind of version this is. enum VersionType { - Derp, + OneSix, Legacy, Nostalgia } type; @@ -66,8 +66,8 @@ struct MinecraftVersion : public BaseVersion } switch (type) { - case Derp: - pre_final.append("Derp"); + case OneSix: + pre_final.append("OneSix"); break; case Legacy: pre_final.append("Legacy"); diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp index f27954a7..2e23ee71 100644 --- a/logic/NostalgiaInstance.cpp +++ b/logic/NostalgiaInstance.cpp @@ -17,7 +17,7 @@ NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) - : DerpInstance(rootDir, settings, parent) + : OneSixInstance(rootDir, settings, parent) { } diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h index 6a6b91a1..a26f7f0a 100644 --- a/logic/NostalgiaInstance.h +++ b/logic/NostalgiaInstance.h @@ -15,9 +15,9 @@ #pragma once -#include "DerpInstance.h" +#include "OneSixInstance.h" -class NostalgiaInstance : public DerpInstance +class NostalgiaInstance : public OneSixInstance { Q_OBJECT public: diff --git a/logic/DerpFTBInstance.cpp b/logic/OneSixFTBInstance.cpp similarity index 69% rename from logic/DerpFTBInstance.cpp rename to logic/OneSixFTBInstance.cpp index 9b687461..71bc1470 100644 --- a/logic/DerpFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -1,17 +1,17 @@ -#include "DerpFTBInstance.h" +#include "OneSixFTBInstance.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "tasks/SequentialTask.h" #include "ForgeInstaller.h" #include "lists/ForgeVersionList.h" #include "MultiMC.h" -class DerpFTBInstanceForge : public Task +class OneSixFTBInstanceForge : public Task { Q_OBJECT public: - explicit DerpFTBInstanceForge(const QString &version, DerpFTBInstance *inst, QObject *parent = 0) : + explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) : Task(parent), instance(inst), version("Forge " + version) { } @@ -38,7 +38,7 @@ public: fjob = new NetJob("Forge download"); fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry)); connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);}); - connect(fjob, &NetJob::succeeded, this, &DerpFTBInstanceForge::installForge); + connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge); connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); }); fjob->start(); } @@ -70,41 +70,41 @@ slots: } private: - DerpFTBInstance *instance; + OneSixFTBInstance *instance; QString version; ForgeVersionPtr forgeVersion; MetaEntryPtr entry; NetJob *fjob; }; -DerpFTBInstance::DerpFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : - DerpInstance(rootDir, settings, parent) +OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + OneSixInstance(rootDir, settings, parent) { QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json")); if (f.open(QFile::ReadOnly)) { QString data = QString::fromUtf8(f.readAll()); QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data); - m_forge.reset(new DerpLibrary(match.captured())); + m_forge.reset(new OneSixLibrary(match.captured())); m_forge->finalize(); } } -QString DerpFTBInstance::id() const +QString OneSixFTBInstance::id() const { return "FTB/" + BaseInstance::id(); } -QString DerpFTBInstance::getStatusbarDescription() +QString OneSixFTBInstance::getStatusbarDescription() { - return "Derp FTB: " + intendedVersionId(); + return "OneSix FTB: " + intendedVersionId(); } -bool DerpFTBInstance::menuActionEnabled(QString action_name) const +bool OneSixFTBInstance::menuActionEnabled(QString action_name) const { return false; } -std::shared_ptr DerpFTBInstance::doUpdate(bool only_prepare) +std::shared_ptr OneSixFTBInstance::doUpdate(bool only_prepare) { std::shared_ptr task; task.reset(new SequentialTask(this)); @@ -112,12 +112,12 @@ std::shared_ptr DerpFTBInstance::doUpdate(bool only_prepare) { task->addTask(std::shared_ptr(MMC->forgelist()->getLoadTask())); } - task->addTask(DerpInstance::doUpdate(only_prepare)); - task->addTask(std::shared_ptr(new DerpFTBInstanceForge(m_forge->version(), this, this))); + task->addTask(OneSixInstance::doUpdate(only_prepare)); + task->addTask(std::shared_ptr(new OneSixFTBInstanceForge(m_forge->version(), this, this))); //FIXME: yes. this may appear dumb. but the previous step can change the list, so we do it all again. //TODO: Add a graph task. Construct graphs of tasks so we may capture the logic properly. - task->addTask(DerpInstance::doUpdate(only_prepare)); + task->addTask(OneSixInstance::doUpdate(only_prepare)); return task; } -#include "DerpFTBInstance.moc" +#include "OneSixFTBInstance.moc" diff --git a/logic/DerpFTBInstance.h b/logic/OneSixFTBInstance.h similarity index 56% rename from logic/DerpFTBInstance.h rename to logic/OneSixFTBInstance.h index c16998bf..dc028819 100644 --- a/logic/DerpFTBInstance.h +++ b/logic/OneSixFTBInstance.h @@ -1,14 +1,14 @@ #pragma once -#include "DerpInstance.h" +#include "OneSixInstance.h" -class DerpLibrary; +class OneSixLibrary; -class DerpFTBInstance : public DerpInstance +class OneSixFTBInstance : public OneSixInstance { Q_OBJECT public: - explicit DerpFTBInstance(const QString &rootDir, SettingsObject *settings, + explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent = 0); virtual QString getStatusbarDescription(); virtual bool menuActionEnabled(QString action_name) const; @@ -18,5 +18,5 @@ public: virtual QString id() const; private: - std::shared_ptr m_forge; + std::shared_ptr m_forge; }; diff --git a/logic/DerpInstance.cpp b/logic/OneSixInstance.cpp similarity index 80% rename from logic/DerpInstance.cpp rename to logic/OneSixInstance.cpp index e6b92b1f..c43a5187 100644 --- a/logic/DerpInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -13,28 +13,28 @@ * limitations under the License. */ -#include "DerpInstance.h" +#include "OneSixInstance.h" #include -#include "DerpInstance_p.h" -#include "DerpUpdate.h" -#include "DerpVersion.h" +#include "OneSixInstance_p.h" +#include "OneSixUpdate.h" +#include "OneSixVersion.h" #include "pathutils.h" #include "logger/QsLog.h" #include "assets/AssetsUtils.h" #include "MultiMC.h" #include "icons/IconList.h" #include "MinecraftProcess.h" -#include "gui/dialogs/DerpModEditDialog.h" +#include "gui/dialogs/OneSixModEditDialog.h" -DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) - : BaseInstance(new DerpInstancePrivate(), rootDir, settings, parent) +OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) + : BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent) { - I_D(DerpInstance); + I_D(OneSixInstance); d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); - d->version.reset(new DerpVersion(this, this)); + d->version.reset(new OneSixVersion(this, this)); if (QDir(instanceRoot()).exists("version.json")) { reloadFullVersion(); @@ -45,9 +45,9 @@ DerpInstance::DerpInstance(const QString &rootDir, SettingsObject *settings, QOb } } -std::shared_ptr DerpInstance::doUpdate(bool only_prepare) +std::shared_ptr OneSixInstance::doUpdate(bool only_prepare) { - return std::shared_ptr(new DerpUpdate(this, only_prepare)); + return std::shared_ptr(new OneSixUpdate(this, only_prepare)); } QString replaceTokensIn(QString text, QMap with) @@ -74,7 +74,7 @@ QString replaceTokensIn(QString text, QMap with) return result; } -QDir DerpInstance::reconstructAssets(std::shared_ptr version) +QDir OneSixInstance::reconstructAssets(std::shared_ptr version) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); @@ -134,9 +134,9 @@ QDir DerpInstance::reconstructAssets(std::shared_ptr version) return virtualRoot; } -QStringList DerpInstance::processMinecraftArgs(MojangAccountPtr account) +QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) { - I_D(DerpInstance); + I_D(OneSixInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; @@ -186,9 +186,9 @@ QStringList DerpInstance::processMinecraftArgs(MojangAccountPtr account) return parts; } -MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) +MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) { - I_D(DerpInstance); + I_D(OneSixInstance); QIcon icon = MMC->icons()->getIcon(iconKey()); auto pixmap = icon.pixmap(128, 128); @@ -248,16 +248,16 @@ MinecraftProcess *DerpInstance::prepareForLaunch(MojangAccountPtr account) return proc; } -void DerpInstance::cleanupAfterRun() +void OneSixInstance::cleanupAfterRun() { QString target_dir = PathCombine(instanceRoot(), "natives/"); QDir dir(target_dir); dir.removeRecursively(); } -std::shared_ptr DerpInstance::loaderModList() +std::shared_ptr OneSixInstance::loaderModList() { - I_D(DerpInstance); + I_D(OneSixInstance); if (!d->loader_mod_list) { d->loader_mod_list.reset(new ModList(loaderModsDir())); @@ -266,9 +266,9 @@ std::shared_ptr DerpInstance::loaderModList() return d->loader_mod_list; } -std::shared_ptr DerpInstance::resourcePackList() +std::shared_ptr OneSixInstance::resourcePackList() { - I_D(DerpInstance); + I_D(OneSixInstance); if (!d->resource_pack_list) { d->resource_pack_list.reset(new ModList(resourcePacksDir())); @@ -277,12 +277,12 @@ std::shared_ptr DerpInstance::resourcePackList() return d->resource_pack_list; } -QDialog *DerpInstance::createModEditDialog(QWidget *parent) +QDialog *OneSixInstance::createModEditDialog(QWidget *parent) { - return new DerpModEditDialog(this, parent); + return new OneSixModEditDialog(this, parent); } -bool DerpInstance::setIntendedVersionId(QString version) +bool OneSixInstance::setIntendedVersionId(QString version) { settings().set("IntendedVersion", version); setShouldUpdate(true); @@ -291,17 +291,17 @@ bool DerpInstance::setIntendedVersionId(QString version) return true; } -QString DerpInstance::intendedVersionId() const +QString OneSixInstance::intendedVersionId() const { return settings().get("IntendedVersion").toString(); } -void DerpInstance::setShouldUpdate(bool val) +void OneSixInstance::setShouldUpdate(bool val) { settings().set("ShouldUpdate", val); } -bool DerpInstance::shouldUpdate() const +bool OneSixInstance::shouldUpdate() const { QVariant var = settings().get("ShouldUpdate"); if (!var.isValid() || var.toBool() == false) @@ -311,60 +311,60 @@ bool DerpInstance::shouldUpdate() const return true; } -bool DerpInstance::versionIsCustom() +bool OneSixInstance::versionIsCustom() { QDir patches(PathCombine(instanceRoot(), "patches/")); return QFile::exists(PathCombine(instanceRoot(), "custom.json")) || (patches.exists() && patches.count() >= 0); } -QString DerpInstance::currentVersionId() const +QString OneSixInstance::currentVersionId() const { return intendedVersionId(); } -bool DerpInstance::reloadFullVersion(QWidget *widgetParent) +bool OneSixInstance::reloadFullVersion(QWidget *widgetParent) { - I_D(DerpInstance); + I_D(OneSixInstance); bool ret = d->version->reload(widgetParent); emit versionReloaded(); return ret; } -void DerpInstance::clearFullVersion() +void OneSixInstance::clearFullVersion() { - I_D(DerpInstance); + I_D(OneSixInstance); d->version->clear(); emit versionReloaded(); } -std::shared_ptr DerpInstance::getFullVersion() +std::shared_ptr OneSixInstance::getFullVersion() { - I_D(DerpInstance); + I_D(OneSixInstance); return d->version; } -QString DerpInstance::defaultBaseJar() const +QString OneSixInstance::defaultBaseJar() const { return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; } -QString DerpInstance::defaultCustomBaseJar() const +QString OneSixInstance::defaultCustomBaseJar() const { return PathCombine(instanceRoot(), "custom.jar"); } -bool DerpInstance::menuActionEnabled(QString action_name) const +bool OneSixInstance::menuActionEnabled(QString action_name) const { if (action_name == "actionChangeInstLWJGLVersion") return false; return true; } -QString DerpInstance::getStatusbarDescription() +QString OneSixInstance::getStatusbarDescription() { - QString descr = "Derp : " + intendedVersionId(); + QString descr = "OneSix : " + intendedVersionId(); if (versionIsCustom()) { descr + " (custom)"; @@ -372,17 +372,17 @@ QString DerpInstance::getStatusbarDescription() return descr; } -QString DerpInstance::loaderModsDir() const +QString OneSixInstance::loaderModsDir() const { return PathCombine(minecraftRoot(), "mods"); } -QString DerpInstance::resourcePacksDir() const +QString OneSixInstance::resourcePacksDir() const { return PathCombine(minecraftRoot(), "resourcepacks"); } -QString DerpInstance::instanceConfigFolder() const +QString OneSixInstance::instanceConfigFolder() const { return PathCombine(minecraftRoot(), "config"); } diff --git a/logic/DerpInstance.h b/logic/OneSixInstance.h similarity index 89% rename from logic/DerpInstance.h rename to logic/OneSixInstance.h index 46f953c0..94577828 100644 --- a/logic/DerpInstance.h +++ b/logic/OneSixInstance.h @@ -17,14 +17,14 @@ #include "BaseInstance.h" -#include "DerpVersion.h" +#include "OneSixVersion.h" #include "ModList.h" -class DerpInstance : public BaseInstance +class OneSixInstance : public BaseInstance { Q_OBJECT public: - explicit DerpInstance(const QString &rootDir, SettingsObject *settings, + explicit OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent = 0); ////// Mod Lists ////// @@ -56,7 +56,7 @@ public: /// clears all version information in preparation for an update void clearFullVersion(); /// get the current full version info - std::shared_ptr getFullVersion(); + std::shared_ptr getFullVersion(); /// is the current version original, or custom? virtual bool versionIsCustom() override; @@ -71,5 +71,5 @@ signals: private: QStringList processMinecraftArgs(MojangAccountPtr account); - QDir reconstructAssets(std::shared_ptr version); + QDir reconstructAssets(std::shared_ptr version); }; diff --git a/logic/DerpInstance_p.h b/logic/OneSixInstance_p.h similarity index 85% rename from logic/DerpInstance_p.h rename to logic/OneSixInstance_p.h index 41f7b62d..a2b70ac9 100644 --- a/logic/DerpInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -16,12 +16,12 @@ #pragma once #include "BaseInstance_p.h" -#include "DerpVersion.h" +#include "OneSixVersion.h" #include "ModList.h" -struct DerpInstancePrivate : public BaseInstancePrivate +struct OneSixInstancePrivate : public BaseInstancePrivate { - std::shared_ptr version; + std::shared_ptr version; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; }; diff --git a/logic/DerpLibrary.cpp b/logic/OneSixLibrary.cpp similarity index 86% rename from logic/DerpLibrary.cpp rename to logic/OneSixLibrary.cpp index ba4d516b..4d892d53 100644 --- a/logic/DerpLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -15,15 +15,15 @@ #include -#include "DerpLibrary.h" -#include "DerpRule.h" +#include "OneSixLibrary.h" +#include "OneSixRule.h" #include "OpSys.h" #include "logic/net/URLConstants.h" #include #include #include "logger/QsLog.h" -void DerpLibrary::finalize() +void OneSixLibrary::finalize() { QStringList parts = m_name.split(':'); QString relative = parts[0]; @@ -76,67 +76,67 @@ void DerpLibrary::finalize() } } -void DerpLibrary::setName(const QString &name) +void OneSixLibrary::setName(const QString &name) { m_name = name; } -void DerpLibrary::setBaseUrl(const QString &base_url) +void OneSixLibrary::setBaseUrl(const QString &base_url) { m_base_url = base_url; } -void DerpLibrary::setIsNative() +void OneSixLibrary::setIsNative() { m_is_native = true; } -void DerpLibrary::addNative(OpSys os, const QString &suffix) +void OneSixLibrary::addNative(OpSys os, const QString &suffix) { m_is_native = true; m_native_suffixes[os] = suffix; } -void DerpLibrary::setRules(QList> rules) +void OneSixLibrary::setRules(QList> rules) { m_rules = rules; } -bool DerpLibrary::isActive() const +bool OneSixLibrary::isActive() const { return m_is_active; } -bool DerpLibrary::isNative() const +bool OneSixLibrary::isNative() const { return m_is_native; } -QString DerpLibrary::downloadUrl() const +QString OneSixLibrary::downloadUrl() const { if (m_absolute_url.size()) return m_absolute_url; return m_download_url; } -QString DerpLibrary::storagePath() const +QString OneSixLibrary::storagePath() const { return m_storage_path; } -void DerpLibrary::setAbsoluteUrl(const QString &absolute_url) +void OneSixLibrary::setAbsoluteUrl(const QString &absolute_url) { m_absolute_url = absolute_url; } -QString DerpLibrary::absoluteUrl() const +QString OneSixLibrary::absoluteUrl() const { return m_absolute_url; } -void DerpLibrary::setHint(const QString &hint) +void OneSixLibrary::setHint(const QString &hint) { m_hint = hint; } -QString DerpLibrary::hint() const +QString OneSixLibrary::hint() const { return m_hint; } -bool DerpLibrary::filesExist() +bool OneSixLibrary::filesExist() { QString storage = storagePath(); if (storage.contains("${arch}")) @@ -167,7 +167,7 @@ bool DerpLibrary::filesExist() return true; } -bool DerpLibrary::extractTo(QString target_dir) +bool OneSixLibrary::extractTo(QString target_dir) { QString storage = storagePath(); if (storage.contains("${arch}")) @@ -220,7 +220,7 @@ bool DerpLibrary::extractTo(QString target_dir) return true; } -QJsonObject DerpLibrary::toJson() +QJsonObject OneSixLibrary::toJson() { QJsonObject libRoot; libRoot.insert("name", m_name); diff --git a/logic/DerpLibrary.h b/logic/OneSixLibrary.h similarity index 98% rename from logic/DerpLibrary.h rename to logic/OneSixLibrary.h index d1cee843..52f89969 100644 --- a/logic/DerpLibrary.h +++ b/logic/OneSixLibrary.h @@ -26,7 +26,7 @@ class Rule; -class DerpLibrary +class OneSixLibrary { private: // basic values used internally (so far) @@ -63,7 +63,7 @@ public: public: /// Constructor - DerpLibrary(const QString &name) + OneSixLibrary(const QString &name) { m_name = name; } diff --git a/logic/DerpRule.cpp b/logic/OneSixRule.cpp similarity index 98% rename from logic/DerpRule.cpp rename to logic/OneSixRule.cpp index 5ed200e3..d8d13b50 100644 --- a/logic/DerpRule.cpp +++ b/logic/OneSixRule.cpp @@ -16,7 +16,7 @@ #include #include -#include "DerpRule.h" +#include "OneSixRule.h" QList> rulesFromJsonV4(const QJsonObject &objectWithRules) { diff --git a/logic/DerpRule.h b/logic/OneSixRule.h similarity index 90% rename from logic/DerpRule.h rename to logic/OneSixRule.h index 0254028c..426e2886 100644 --- a/logic/DerpRule.h +++ b/logic/OneSixRule.h @@ -17,7 +17,7 @@ #include -#include "logic/DerpLibrary.h" +#include "logic/OneSixLibrary.h" enum RuleAction { @@ -33,7 +33,7 @@ class Rule { protected: RuleAction m_result; - virtual bool applies(DerpLibrary *parent) = 0; + virtual bool applies(OneSixLibrary *parent) = 0; public: Rule(RuleAction result) : m_result(result) @@ -41,7 +41,7 @@ public: } virtual ~Rule() {}; virtual QJsonObject toJson() = 0; - RuleAction apply(DerpLibrary *parent) + RuleAction apply(OneSixLibrary *parent) { if (applies(parent)) return m_result; @@ -60,7 +60,7 @@ private: QString m_version_regexp; protected: - virtual bool applies(DerpLibrary *) + virtual bool applies(OneSixLibrary *) { return (m_system == currentSystem); } @@ -81,7 +81,7 @@ public: class ImplicitRule : public Rule { protected: - virtual bool applies(DerpLibrary *) + virtual bool applies(OneSixLibrary *) { return true; } diff --git a/logic/DerpUpdate.cpp b/logic/OneSixUpdate.cpp similarity index 88% rename from logic/DerpUpdate.cpp rename to logic/OneSixUpdate.cpp index 5686e4ac..f51b6c23 100644 --- a/logic/DerpUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -14,7 +14,7 @@ */ #include "MultiMC.h" -#include "DerpUpdate.h" +#include "OneSixUpdate.h" #include @@ -25,9 +25,9 @@ #include "BaseInstance.h" #include "lists/MinecraftVersionList.h" -#include "DerpVersion.h" -#include "DerpLibrary.h" -#include "DerpInstance.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "OneSixInstance.h" #include "net/ForgeMirrors.h" #include "net/URLConstants.h" #include "assets/AssetsUtils.h" @@ -35,12 +35,12 @@ #include "pathutils.h" #include -DerpUpdate::DerpUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) +OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) : Task(parent), m_inst(inst), m_only_prepare(only_prepare) { } -void DerpUpdate::executeTask() +void OneSixUpdate::executeTask() { QString intendedVersion = m_inst->intendedVersionId(); @@ -77,7 +77,7 @@ void DerpUpdate::executeTask() } } -void DerpUpdate::versionFileStart() +void OneSixUpdate::versionFileStart() { QLOG_INFO() << m_inst->name() << ": getting version file."; setStatus(tr("Getting the version files from Mojang...")); @@ -94,10 +94,10 @@ void DerpUpdate::versionFileStart() specificVersionDownloadJob->start(); } -void DerpUpdate::versionFileFinished() +void OneSixUpdate::versionFileFinished() { NetActionPtr DlJob = specificVersionDownloadJob->first(); - DerpInstance *inst = (DerpInstance *)m_inst; + OneSixInstance *inst = (OneSixInstance *)m_inst; QString version_id = targetVersion->descriptor(); QString inst_dir = m_inst->instanceRoot(); @@ -142,16 +142,16 @@ void DerpUpdate::versionFileFinished() jarlibStart(); } -void DerpUpdate::versionFileFailed() +void OneSixUpdate::versionFileFailed() { emitFailed("Failed to download the version description. Try again."); } -void DerpUpdate::assetIndexStart() +void OneSixUpdate::assetIndexStart() { setStatus(tr("Updating assets index...")); - DerpInstance *inst = (DerpInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); + OneSixInstance *inst = (OneSixInstance *)m_inst; + std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; QString localPath = assetName + ".json"; @@ -170,12 +170,12 @@ void DerpUpdate::assetIndexStart() jarlibDownloadJob->start(); } -void DerpUpdate::assetIndexFinished() +void OneSixUpdate::assetIndexFinished() { AssetsIndex index; - DerpInstance *inst = (DerpInstance *)m_inst; - std::shared_ptr version = inst->getFullVersion(); + OneSixInstance *inst = (OneSixInstance *)m_inst; + std::shared_ptr version = inst->getFullVersion(); QString assetName = version->assets; QString asset_fname = "assets/indexes/" + assetName + ".json"; @@ -215,26 +215,26 @@ void DerpUpdate::assetIndexFinished() assetsFinished(); } -void DerpUpdate::assetIndexFailed() +void OneSixUpdate::assetIndexFailed() { emitFailed("Failed to download the assets index!"); } -void DerpUpdate::assetsFinished() +void OneSixUpdate::assetsFinished() { prepareForLaunch(); } -void DerpUpdate::assetsFailed() +void OneSixUpdate::assetsFailed() { emitFailed("Failed to download assets!"); } -void DerpUpdate::jarlibStart() +void OneSixUpdate::jarlibStart() { setStatus(tr("Getting the library files from Mojang...")); QLOG_INFO() << m_inst->name() << ": downloading libraries"; - DerpInstance *inst = (DerpInstance *)m_inst; + OneSixInstance *inst = (OneSixInstance *)m_inst; bool successful = inst->reloadFullVersion(); if (!successful) { @@ -244,7 +244,7 @@ void DerpUpdate::jarlibStart() } // Build a list of URLs that will need to be downloaded. - std::shared_ptr version = inst->getFullVersion(); + std::shared_ptr version = inst->getFullVersion(); // minecraft.jar for this version { QString version_id = version->id; @@ -318,12 +318,12 @@ void DerpUpdate::jarlibStart() jarlibDownloadJob->start(); } -void DerpUpdate::jarlibFinished() +void OneSixUpdate::jarlibFinished() { assetIndexStart(); } -void DerpUpdate::jarlibFailed() +void OneSixUpdate::jarlibFailed() { QStringList failed = jarlibDownloadJob->getFailedFiles(); QString failed_all = failed.join("\n"); @@ -331,17 +331,17 @@ void DerpUpdate::jarlibFailed() "\n\nPlease try again."); } -void DerpUpdate::prepareForLaunch() +void OneSixUpdate::prepareForLaunch() { setStatus(tr("Preparing for launch...")); QLOG_INFO() << m_inst->name() << ": preparing for launch"; - auto derp_inst = (DerpInstance *)m_inst; + auto OneSix_inst = (OneSixInstance *)m_inst; // delete any leftovers, if they are present. - derp_inst->cleanupAfterRun(); + OneSix_inst->cleanupAfterRun(); - QString natives_dir_raw = PathCombine(derp_inst->instanceRoot(), "natives/"); - auto version = derp_inst->getFullVersion(); + QString natives_dir_raw = PathCombine(OneSix_inst->instanceRoot(), "natives/"); + auto version = OneSix_inst->getFullVersion(); if (!version) { emitFailed("The version information for this instance is not complete. Try re-creating " diff --git a/logic/DerpUpdate.h b/logic/OneSixUpdate.h similarity index 92% rename from logic/DerpUpdate.h rename to logic/OneSixUpdate.h index 475f6c35..bc717a94 100644 --- a/logic/DerpUpdate.h +++ b/logic/OneSixUpdate.h @@ -25,11 +25,11 @@ class MinecraftVersion; class BaseInstance; -class DerpUpdate : public Task +class OneSixUpdate : public Task { Q_OBJECT public: - explicit DerpUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); + explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); virtual void executeTask(); private diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 3571be9b..3db3bd77 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -13,228 +13,60 @@ * limitations under the License. */ -#include "logic/DerpVersion.h" -#include "logic/DerpLibrary.h" -#include "logic/DerpRule.h" +#include "OneSixVersion.h" -#include "logger/QsLog.h" +#include -std::shared_ptr fromJsonV4(QJsonObject root, - std::shared_ptr fullVersion) +#include "OneSixVersionBuilder.h" + +OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) + : QAbstractListModel(parent), m_instance(instance) { - fullVersion->id = root.value("id").toString(); - - fullVersion->mainClass = root.value("mainClass").toString(); - auto procArgsValue = root.value("processArguments"); - if (procArgsValue.isString()) - { - fullVersion->processArguments = procArgsValue.toString(); - QString toCompare = fullVersion->processArguments.toLower(); - if (toCompare == "legacy") - { - fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - fullVersion->minecraftArguments = - "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - fullVersion->minecraftArguments = "--username ${auth_player_name} " - "--session ${auth_session} " - "--version ${profile_name}"; - } - } - - auto minecraftArgsValue = root.value("minecraftArguments"); - if (minecraftArgsValue.isString()) - { - fullVersion->minecraftArguments = minecraftArgsValue.toString(); - } - - auto minecraftTypeValue = root.value("type"); - if (minecraftTypeValue.isString()) - { - fullVersion->type = minecraftTypeValue.toString(); - } - - fullVersion->releaseTime = root.value("releaseTime").toString(); - fullVersion->time = root.value("time").toString(); - - auto assetsID = root.value("assets"); - if (assetsID.isString()) - { - fullVersion->assets = assetsID.toString(); - } - else - { - fullVersion->assets = "legacy"; - } - - QLOG_DEBUG() << "Assets version:" << fullVersion->assets; - - // Iterate through the list, if it's a list. - auto librariesValue = root.value("libraries"); - if (!librariesValue.isArray()) - return fullVersion; - - QJsonArray libList = root.value("libraries").toArray(); - for (auto libVal : libList) - { - if (!libVal.isObject()) - { - continue; - } - - QJsonObject libObj = libVal.toObject(); - - // Library name - auto nameVal = libObj.value("name"); - if (!nameVal.isString()) - continue; - std::shared_ptr library(new DerpLibrary(nameVal.toString())); - - auto urlVal = libObj.value("url"); - if (urlVal.isString()) - { - library->setBaseUrl(urlVal.toString()); - } - auto hintVal = libObj.value("MMC-hint"); - if (hintVal.isString()) - { - library->setHint(hintVal.toString()); - } - auto urlAbsVal = libObj.value("MMC-absoluteUrl"); - auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility - if (urlAbsVal.isString()) - { - library->setAbsoluteUrl(urlAbsVal.toString()); - } - else if (urlAbsuVal.isString()) - { - library->setAbsoluteUrl(urlAbsuVal.toString()); - } - // Extract excludes (if any) - auto extractVal = libObj.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) - { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) - { - if (excludeVal.isString()) - excludes.append(excludeVal.toString()); - } - library->extract_excludes = excludes; - } - } - - auto nativesVal = libObj.value("natives"); - if (nativesVal.isObject()) - { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - auto iter = nativesObj.begin(); - while (iter != nativesObj.end()) - { - auto osType = OpSys_fromString(iter.key()); - if (osType == Os_Other) - continue; - if (!iter.value().isString()) - continue; - library->addNative(osType, iter.value().toString()); - iter++; - } - } - library->setRules(rulesFromJsonV4(libObj)); - library->finalize(); - fullVersion->libraries.append(library); - } - return fullVersion; + clear(); } -std::shared_ptr DerpVersion::fromJson(QJsonObject root) +bool OneSixVersion::reload(QWidget *widgetParent) { - std::shared_ptr readVersion(new DerpVersion()); - int launcher_ver = readVersion->minimumLauncherVersion = - root.value("minimumLauncherVersion").toDouble(); - - // ADD MORE HERE :D - if (launcher_ver > 0 && launcher_ver <= 13) - return fromJsonV4(root, readVersion); - else - { - return std::shared_ptr(); - } + return OneSixVersionBuilder::build(this, m_instance, widgetParent); } -std::shared_ptr DerpVersion::fromFile(QString filepath) +void OneSixVersion::clear() { - QFile file(filepath); - if (!file.open(QIODevice::ReadOnly)) - { - return std::shared_ptr(); - } - - auto data = file.readAll(); - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - return std::shared_ptr(); - } - - if (!jsonDoc.isObject()) - { - return std::shared_ptr(); - } - QJsonObject root = jsonDoc.object(); - auto version = fromJson(root); - if (version) - version->original_file = filepath; - return version; + id.clear(); + time.clear(); + releaseTime.clear(); + type.clear(); + assets.clear(); + processArguments.clear(); + minecraftArguments.clear(); + minimumLauncherVersion = 0xDEADBEAF; + mainClass.clear(); + libraries.clear(); } -bool DerpVersion::toOriginalFile() +void OneSixVersion::dump() const { - if (original_file.isEmpty()) - return false; - QSaveFile file(original_file); - if (!file.open(QIODevice::WriteOnly)) + qDebug().nospace() << "OneSixVersion(" + << "\n\tid=" << id + << "\n\ttime=" << time + << "\n\treleaseTime=" << releaseTime + << "\n\ttype=" << type + << "\n\tassets=" << assets + << "\n\tprocessArguments=" << processArguments + << "\n\tminecraftArguments=" << minecraftArguments + << "\n\tminimumLauncherVersion=" << minimumLauncherVersion + << "\n\tmainClass=" << mainClass + << "\n\tlibraries="; + for (auto lib : libraries) { - return false; + qDebug().nospace() << "\n\t\t" << lib.get(); } - // serialize base attributes (those we care about anyway) - QJsonObject root; - root.insert("minecraftArguments", minecraftArguments); - root.insert("mainClass", mainClass); - root.insert("minimumLauncherVersion", minimumLauncherVersion); - root.insert("time", time); - root.insert("id", id); - root.insert("type", type); - // screw processArguments - root.insert("releaseTime", releaseTime); - QJsonArray libarray; - for (const auto &lib : libraries) - { - libarray.append(lib->toJson()); - } - if (libarray.count()) - root.insert("libraries", libarray); - QJsonDocument doc(root); - file.write(doc.toJson()); - return file.commit(); + qDebug().nospace() << "\n)"; } -QList> DerpVersion::getActiveNormalLibs() +QList > OneSixVersion::getActiveNormalLibs() { - QList> output; + QList > output; for (auto lib : libraries) { if (lib->isActive() && !lib->isNative()) @@ -245,9 +77,9 @@ QList> DerpVersion::getActiveNormalLibs() return output; } -QList> DerpVersion::getActiveNativeLibs() +QList > OneSixVersion::getActiveNativeLibs() { - QList> output; + QList > output; for (auto lib : libraries) { if (lib->isActive() && lib->isNative()) @@ -258,17 +90,17 @@ QList> DerpVersion::getActiveNativeLibs() return output; } -void DerpVersion::externalUpdateStart() +std::shared_ptr OneSixVersion::fromJson(const QJsonObject &obj) { - beginResetModel(); + std::shared_ptr version(new OneSixVersion(0)); + if (OneSixVersionBuilder::read(version.get(), obj)) + { + return version; + } + return 0; } -void DerpVersion::externalUpdateFinish() -{ - endResetModel(); -} - -QVariant DerpVersion::data(const QModelIndex &index, int role) const +QVariant OneSixVersion::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -296,7 +128,7 @@ QVariant DerpVersion::data(const QModelIndex &index, int role) const return QVariant(); } -Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const +Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; @@ -312,7 +144,7 @@ Qt::ItemFlags DerpVersion::flags(const QModelIndex &index) const // return QAbstractListModel::flags(index); } -QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int role) const +QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); @@ -329,12 +161,34 @@ QVariant DerpVersion::headerData(int section, Qt::Orientation orientation, int r } } -int DerpVersion::rowCount(const QModelIndex &parent) const +int OneSixVersion::rowCount(const QModelIndex &parent) const { return libraries.size(); } -int DerpVersion::columnCount(const QModelIndex &parent) const +int OneSixVersion::columnCount(const QModelIndex &parent) const { return 3; } + +QDebug operator<<(QDebug &dbg, const OneSixVersion *version) +{ + version->dump(); + return dbg.maybeSpace(); +} +QDebug operator<<(QDebug &dbg, const OneSixLibrary *library) +{ + dbg.nospace() << "OneSixLibrary(" + << "\n\t\t\trawName=" << library->rawName() + << "\n\t\t\tname=" << library->name() + << "\n\t\t\tversion=" << library->version() + << "\n\t\t\ttype=" << library->type() + << "\n\t\t\tisActive=" << library->isActive() + << "\n\t\t\tisNative=" << library->isNative() + << "\n\t\t\tdownloadUrl=" << library->downloadUrl() + << "\n\t\t\tstoragePath=" << library->storagePath() + << "\n\t\t\tabsolutePath=" << library->absoluteUrl() + << "\n\t\t\thint=" << library->hint(); + dbg.nospace() << "\n\t\t)"; + return dbg.maybeSpace(); +} diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index e8f744aa..00afd010 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -14,15 +14,23 @@ */ #pragma once -#include + +#include + +#include +#include #include -class DerpLibrary; +#include "OneSixLibrary.h" -class DerpVersion : public QAbstractListModel +class OneSixInstance; + +class OneSixVersion : public QAbstractListModel { - // Things required to implement the Qt list model + Q_OBJECT public: + explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0); + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant headerData(int section, Qt::Orientation orientation, @@ -30,24 +38,19 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - // serialization/deserialization -public: - bool toOriginalFile(); - static std::shared_ptr fromJson(QJsonObject root); - static std::shared_ptr fromFile(QString filepath); + bool reload(QWidget *widgetParent); + void clear(); + + void dump() const; public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); - // called when something starts/stops messing with the object - // FIXME: these are ugly in every possible way. - void externalUpdateStart(); - void externalUpdateFinish(); + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); + + static std::shared_ptr fromJson(const QJsonObject &obj); // data members public: - /// file this was read from. blank, if none - QString original_file; /// the ID - determines which jar to use! ACTUALLY IMPORTANT! QString id; /// Last updated time - as a string @@ -81,7 +84,7 @@ public: QString mainClass; /// the list of libs - both active and inactive, native and java - QList> libraries; + QList> libraries; /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. @@ -103,4 +106,10 @@ public: } */ // QList rules; + +private: + OneSixInstance *m_instance; }; + +QDebug operator<<(QDebug &dbg, const OneSixVersion *version); +QDebug operator<<(QDebug &dbg, const OneSixLibrary *library); diff --git a/logic/DerpVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp similarity index 87% rename from logic/DerpVersionBuilder.cpp rename to logic/OneSixVersionBuilder.cpp index 20f43404..85f55a52 100644 --- a/logic/DerpVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "DerpVersionBuilder.h" +#include "OneSixVersionBuilder.h" #include #include @@ -26,35 +26,35 @@ #include #include -#include "DerpVersion.h" -#include "DerpInstance.h" -#include "DerpRule.h" +#include "OneSixVersion.h" +#include "OneSixInstance.h" +#include "OneSixRule.h" #include "logger/QsLog.h" -DerpVersionBuilder::DerpVersionBuilder() +OneSixVersionBuilder::OneSixVersionBuilder() { } -bool DerpVersionBuilder::build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent) +bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent) { - DerpVersionBuilder builder; + OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = instance; builder.m_widgetParent = widgetParent; return builder.build(); } -bool DerpVersionBuilder::read(DerpVersion *version, const QJsonObject &obj) +bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) { - DerpVersionBuilder builder; + OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = 0; builder.m_widgetParent = 0; return builder.read(obj); } -bool DerpVersionBuilder::build() +bool OneSixVersionBuilder::build() { m_version->clear(); @@ -127,7 +127,7 @@ bool DerpVersionBuilder::build() return true; } -bool DerpVersionBuilder::read(const QJsonObject &obj) +bool OneSixVersionBuilder::read(const QJsonObject &obj) { m_version->clear(); @@ -152,14 +152,14 @@ void applyString(const QJsonObject &obj, const QString &key, QString &out, const } } } -void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(DerpLibrary::*func)(const QString &val)) +void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(OneSixLibrary::*func)(const QString &val)) { if (obj.contains(key) && obj.value(key).isString()) { (lib.get()->*func)(obj.value(key).toString()); } } -bool DerpVersionBuilder::apply(const QJsonObject &object) +bool OneSixVersionBuilder::apply(const QJsonObject &object) { applyString(object, "id", m_version->id); applyString(object, "mainClass", m_version->mainClass); @@ -265,7 +265,7 @@ bool DerpVersionBuilder::apply(const QJsonObject &object) return true; } -int findLibrary(QList > haystack, const QString &needle) +int findLibrary(QList > haystack, const QString &needle) { for (int i = 0; i < haystack.size(); ++i) { @@ -277,7 +277,7 @@ int findLibrary(QList > haystack, const QString &ne return -1; } -bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionBuilder::Type type) +bool OneSixVersionBuilder::applyLibrary(const QJsonObject &lib, const OneSixVersionBuilder::Type type) { // Library name auto nameVal = lib.value("name"); @@ -302,11 +302,11 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB return false; } - std::shared_ptr library; + std::shared_ptr library; if (lib.value("insert").toString() != "apply" && type == Add) { - QMutableListIterator > it(m_version->libraries); + QMutableListIterator > it(m_version->libraries); while (it.hasNext()) { if (it.next()->rawName() == name) @@ -322,13 +322,13 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB } else { - library.reset(new DerpLibrary(nameVal.toString())); + library.reset(new OneSixLibrary(nameVal.toString())); } - applyString(lib, "url", library, &DerpLibrary::setBaseUrl); - applyString(lib, "MMC-hint", library, &DerpLibrary::setHint); - applyString(lib, "MMC-absulute_url", library, &DerpLibrary::setAbsoluteUrl); - applyString(lib, "MMC-absoluteUrl", library, &DerpLibrary::setAbsoluteUrl); + applyString(lib, "url", library, &OneSixLibrary::setBaseUrl); + applyString(lib, "MMC-hint", library, &OneSixLibrary::setHint); + applyString(lib, "MMC-absulute_url", library, &OneSixLibrary::setAbsoluteUrl); + applyString(lib, "MMC-absoluteUrl", library, &OneSixLibrary::setAbsoluteUrl); auto extractVal = lib.value("extract"); if (extractVal.isObject()) @@ -426,7 +426,7 @@ bool DerpVersionBuilder::applyLibrary(const QJsonObject &lib, const DerpVersionB return true; } -bool DerpVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) diff --git a/logic/DerpVersionBuilder.h b/logic/OneSixVersionBuilder.h similarity index 76% rename from logic/DerpVersionBuilder.h rename to logic/OneSixVersionBuilder.h index b94cd8e9..a3ba331c 100644 --- a/logic/DerpVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -17,22 +17,22 @@ #include -class DerpVersion; -class DerpInstance; +class OneSixVersion; +class OneSixInstance; class QWidget; class QJsonObject; class QFileInfo; -class DerpVersionBuilder +class OneSixVersionBuilder { - DerpVersionBuilder(); + OneSixVersionBuilder(); public: - static bool build(DerpVersion *version, DerpInstance *instance, QWidget *widgetParent); - static bool read(DerpVersion *version, const QJsonObject &obj); + static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent); + static bool read(OneSixVersion *version, const QJsonObject &obj); private: - DerpVersion *m_version; - DerpInstance *m_instance; + OneSixVersion *m_version; + OneSixInstance *m_instance; QWidget *m_widgetParent; enum Type diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 29894b5a..91f86df0 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -234,18 +234,18 @@ void MCVListLoadTask::list_downloaded() } // Parse the type. MinecraftVersion::VersionType versionType; - // Derp or Legacy. use filter to determine type + // OneSix or Legacy. use filter to determine type if (versionTypeStr == "release") { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::Derp; + : MinecraftVersion::OneSix; is_latest = (versionID == latestReleaseID); is_snapshot = false; } else if (versionTypeStr == "snapshot") // It's a snapshot... yay { versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy - : MinecraftVersion::Derp; + : MinecraftVersion::OneSix; is_latest = (versionID == latestSnapshotID); is_snapshot = true; } From 0f7b38c76bacc210beae21c4e23cc312f53fa5e4 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 24 Jan 2014 18:17:26 +0100 Subject: [PATCH 19/49] Fix some stuff --- logic/InstanceFactory.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 730bcd69..807bccd0 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -51,7 +51,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst QString inst_type = m_settings->get("InstanceType").toString(); // FIXME: replace with a map lookup, where instance classes register their types - if (inst_type == "OneSix" || inst_type == "OneSix") + if (inst_type == "OneSix") { inst = new OneSixInstance(instDir, m_settings, this); } @@ -67,7 +67,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new LegacyFTBInstance(instDir, m_settings, this); } - else if (inst_type == "OneSixFTB" || inst_type == "OneSixFTB") + else if (inst_type == "OneSixFTB") { inst = new OneSixFTBInstance(instDir, m_settings, this); } @@ -102,7 +102,7 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& switch (mcVer->type) { case MinecraftVersion::Legacy: - // TODO OneSix + // TODO new instance type m_settings->set("InstanceType", "Legacy"); inst = new LegacyInstance(instDir, m_settings, this); inst->setIntendedVersionId(version->descriptor()); @@ -176,7 +176,7 @@ InstanceFactory::InstCreateError InstanceFactory::copyInstance(BaseInstance *&ne m_settings->registerSetting("InstanceType", "Legacy"); QString inst_type = m_settings->get("InstanceType").toString(); - if(inst_type == "OneSixFTB" || inst_type == "OneSixFTB") + if(inst_type == "OneSixFTB") m_settings->set("InstanceType", "OneSix"); if(inst_type == "LegacyFTB") m_settings->set("InstanceType", "Legacy"); From f9ea3dbfdea0db5626a6afadcfd599a5f53d8abd Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 19:20:07 +0100 Subject: [PATCH 20/49] Split parsing/applying. Better error logging. Fix crash. --- gui/dialogs/OneSixModEditDialog.cpp | 4 +- logic/ForgeInstaller.cpp | 49 +- logic/OneSixFTBInstance.cpp | 2 +- logic/OneSixInstance.cpp | 30 +- logic/OneSixInstance.h | 8 +- logic/OneSixInstance_p.h | 1 + logic/OneSixLibrary.cpp | 14 +- logic/OneSixLibrary.h | 2 + logic/OneSixUpdate.cpp | 4 +- logic/OneSixVersion.cpp | 5 +- logic/OneSixVersion.h | 6 +- logic/OneSixVersionBuilder.cpp | 1054 +++++++++++++++++++-------- logic/OneSixVersionBuilder.h | 18 +- 13 files changed, 842 insertions(+), 355 deletions(-) diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index 625a7c91..ad15311b 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -162,7 +162,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked() } } } - m_inst->reloadFullVersion(this); + m_inst->reloadVersion(this); } void OneSixModEditDialog::on_liteloaderBtn_clicked() @@ -184,7 +184,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked() } else { - m_inst->reloadFullVersion(this); + m_inst->reloadVersion(this); } } diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 863c3dfd..c0340e95 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "MultiMC.h" #include "OneSixInstance.h" @@ -146,36 +148,71 @@ bool ForgeInstaller::add(OneSixInstance *to) QJsonObject libObj = lib->toJson(); bool found = false; + bool equals = false; // find an entry that matches this one - for (auto tolib : to->getFullVersion()->libraries) + for (auto tolib : to->getNonCustomVersion()->libraries) { if (tolib->name() != libName) continue; found = true; + if (tolib->toJson() == libObj) + { + equals = true; + } // replace lib libObj.insert("insert", QString("apply")); break; } + if (equals) + { + continue; + } if (!found) { // add lib QJsonObject insertObj; - insertObj.insert("before", to->getFullVersion()->libraries.at(sliding_insert_window)->rawName()); + insertObj.insert( + "before", + to->getFullVersion()->libraries.at(sliding_insert_window + 1)->rawName()); libObj.insert("insert", insertObj); sliding_insert_window++; } - librariesPlus.append(libObj); + librariesPlus.prepend(libObj); } obj.insert("+libraries", librariesPlus); obj.insert("mainClass", m_forge_version->mainClass); - obj.insert("minecraftArguments", m_forge_version->minecraftArguments); - obj.insert("processArguments", m_forge_version->processArguments); + QString args = m_forge_version->minecraftArguments; + QStringList tweakers; + { + QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)"); + QRegularExpressionMatch match = expression.match(args); + while (match.hasMatch()) + { + tweakers.append(match.captured(1)); + args.remove(match.capturedStart(), match.capturedLength()); + match = expression.match(args); + } + } + if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments) + { + obj.insert("minecraftArguments", args); + } + if (!tweakers.isEmpty()) + { + obj.insert("+tweakers", QJsonArray::fromStringList(tweakers)); + } + if (!m_forge_version->processArguments.isEmpty() && + m_forge_version->processArguments != to->getNonCustomVersion()->processArguments) + { + obj.insert("processArguments", m_forge_version->processArguments); + } } QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) { - QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + QLOG_ERROR() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); return false; } file.write(QJsonDocument(obj).toJson()); diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 71bc1470..54bfbe2b 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -55,7 +55,7 @@ slots: setStatus(tr("Installing Forge...")); QString forgePath = entry->getFullPath(); ForgeInstaller forge(forgePath, forgeVersion->universal_url); - if (!instance->reloadFullVersion()) + if (!instance->reloadVersion()) { emitFailed(tr("Couldn't load the version config")); return; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index c43a5187..19d5e112 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -35,13 +35,14 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); d->version.reset(new OneSixVersion(this, this)); + d->nonCustomVersion.reset(new OneSixVersion(this, this)); if (QDir(instanceRoot()).exists("version.json")) { - reloadFullVersion(); + reloadVersion(); } else { - clearFullVersion(); + clearVersion(); } } @@ -139,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) I_D(OneSixInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; + for (auto tweaker : version->tweakers) + { + args_pattern += " --tweakClass " + tweaker; + } QMap token_mapping; // yggdrasil! @@ -287,7 +292,7 @@ bool OneSixInstance::setIntendedVersionId(QString version) settings().set("IntendedVersion", version); setShouldUpdate(true); QFile::remove(PathCombine(instanceRoot(), "version.json")); - clearFullVersion(); + clearVersion(); return true; } @@ -323,28 +328,39 @@ QString OneSixInstance::currentVersionId() const return intendedVersionId(); } -bool OneSixInstance::reloadFullVersion(QWidget *widgetParent) +bool OneSixInstance::reloadVersion(QWidget *widgetParent) { I_D(OneSixInstance); bool ret = d->version->reload(widgetParent); + if (ret) + { + ret = d->nonCustomVersion->reload(widgetParent, true); + } emit versionReloaded(); return ret; } -void OneSixInstance::clearFullVersion() +void OneSixInstance::clearVersion() { I_D(OneSixInstance); d->version->clear(); + d->nonCustomVersion->clear(); emit versionReloaded(); } -std::shared_ptr OneSixInstance::getFullVersion() +std::shared_ptr OneSixInstance::getFullVersion() const { - I_D(OneSixInstance); + I_D(const OneSixInstance); return d->version; } +std::shared_ptr OneSixInstance::getNonCustomVersion() const +{ + I_D(const OneSixInstance); + return d->nonCustomVersion; +} + QString OneSixInstance::defaultBaseJar() const { return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 94577828..ee64e886 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -52,11 +52,13 @@ public: virtual QDialog *createModEditDialog(QWidget *parent) override; /// reload the full version json files. return true on success! - bool reloadFullVersion(QWidget *widgetParent = 0); + bool reloadVersion(QWidget *widgetParent = 0); /// clears all version information in preparation for an update - void clearFullVersion(); + void clearVersion(); /// get the current full version info - std::shared_ptr getFullVersion(); + std::shared_ptr getFullVersion() const; + /// gets the current version info, excluding custom.json + std::shared_ptr getNonCustomVersion() const; /// is the current version original, or custom? virtual bool versionIsCustom() override; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index a2b70ac9..6dc74e50 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -22,6 +22,7 @@ struct OneSixInstancePrivate : public BaseInstancePrivate { std::shared_ptr version; + std::shared_ptr nonCustomVersion; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; }; diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 4d892d53..2b1f0600 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -93,6 +93,10 @@ void OneSixLibrary::addNative(OpSys os, const QString &suffix) m_is_native = true; m_native_suffixes[os] = suffix; } +void OneSixLibrary::clearSuffixes() +{ + m_native_suffixes.clear(); +} void OneSixLibrary::setRules(QList> rules) { m_rules = rules; @@ -176,7 +180,7 @@ bool OneSixLibrary::extractTo(QString target_dir) cooked_storage.replace("${arch}", "32"); QString origin = PathCombine("libraries", cooked_storage); QString target_dir_cooked = PathCombine(target_dir, "32"); - if(!ensureFolderPathExists(target_dir_cooked)) + if (!ensureFolderPathExists(target_dir_cooked)) { QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; return false; @@ -191,7 +195,7 @@ bool OneSixLibrary::extractTo(QString target_dir) cooked_storage.replace("${arch}", "64"); origin = PathCombine("libraries", cooked_storage); target_dir_cooked = PathCombine(target_dir, "64"); - if(!ensureFolderPathExists(target_dir_cooked)) + if (!ensureFolderPathExists(target_dir_cooked)) { QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; return false; @@ -205,7 +209,7 @@ bool OneSixLibrary::extractTo(QString target_dir) } else { - if(!ensureFolderPathExists(target_dir)) + if (!ensureFolderPathExists(target_dir)) { QLOG_ERROR() << "Couldn't create folder " + target_dir; return false; @@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson() libRoot.insert("MMC-hint", m_hint); if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::LIBRARY_BASE) + m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty()) + { libRoot.insert("url", m_base_url); + } if (isNative() && m_native_suffixes.size()) { QJsonObject nativeList; diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 52f89969..5384015a 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -107,6 +107,8 @@ public: void setIsNative(); /// Attach a name suffix to the specified OS native void addNative(OpSys os, const QString &suffix); + /// Clears all suffixes + void clearSuffixes(); /// Set the load rules void setRules(QList> rules); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index f51b6c23..c056bc4c 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -137,7 +137,7 @@ void OneSixUpdate::versionFileFinished() { finfo.remove(); } - inst->reloadFullVersion(); + inst->reloadVersion(); jarlibStart(); } @@ -235,7 +235,7 @@ void OneSixUpdate::jarlibStart() setStatus(tr("Getting the library files from Mojang...")); QLOG_INFO() << m_inst->name() << ": downloading libraries"; OneSixInstance *inst = (OneSixInstance *)m_inst; - bool successful = inst->reloadFullVersion(); + bool successful = inst->reloadVersion(); if (!successful) { emitFailed("Failed to load the version description file. It might be " diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 3db3bd77..1b6bc9cb 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -25,9 +25,9 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) clear(); } -bool OneSixVersion::reload(QWidget *widgetParent) +bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom) { - return OneSixVersionBuilder::build(this, m_instance, widgetParent); + return OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); } void OneSixVersion::clear() @@ -42,6 +42,7 @@ void OneSixVersion::clear() minimumLauncherVersion = 0xDEADBEAF; mainClass.clear(); libraries.clear(); + tweakers.clear(); } void OneSixVersion::dump() const diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 00afd010..767e05db 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -38,7 +38,7 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - bool reload(QWidget *widgetParent); + bool reload(QWidget *widgetParent, const bool excludeCustom = false); void clear(); void dump() const; @@ -78,6 +78,10 @@ public: * writing) */ int minimumLauncherVersion = 0xDEADBEEF; + /** + * A list of all tweaker classes + */ + QStringList tweakers; /** * The main class to load first */ diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 85f55a52..c031a94a 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -31,18 +31,660 @@ #include "OneSixRule.h" #include "logger/QsLog.h" +struct VersionFile +{ + int order; + QString id; + QString mainClass; + QString overwriteMinecraftArguments; + QString addMinecraftArguments; + QString removeMinecraftArguments; + QString processArguments; + QString type; + QString releaseTime; + QString time; + QString assets; + int minimumLauncherVersion = -1; + + bool shouldOverwriteTweakers = false; + QStringList overwriteTweakers; + QStringList addTweakers; + QStringList removeTweakers; + + struct Library + { + QString name; + QString url; + QString hint; + QString absoluteUrl; + bool applyExcludes = false; + QStringList excludes; + bool applyNatives = false; + QList> natives; + bool applyRules = false; + QList> rules; + + // user for '+' libraries + enum InsertType + { + Apply, + Append, + Prepend, + InsertBefore, + InsertAfter, + Replace + }; + InsertType insertType; + QString insertData; + }; + bool shouldOverwriteLibs = false; + QList overwriteLibs; + QList addLibs; + QList removeLibs; + + static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename, + bool &isError) + { + isError = true; + Library out; + if (!libObj.contains("name")) + { + QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field"; + return out; + } + out.name = libObj.value("name").toString(); + + auto readString = [libObj, filename](const QString &key, QString &variable) + { + if (libObj.contains(key)) + { + QJsonValue val = libObj.value(key); + if (!val.isString()) + { + QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; + } + else + { + variable = val.toString(); + } + } + }; + + readString("url", out.url); + readString("MMC-hint", out.hint); + readString("MMC-absulute_url", out.absoluteUrl); + readString("MMC-absoluteUrl", out.absoluteUrl); + if (libObj.contains("extract")) + { + if (!libObj.value("extract").isObject()) + { + QLOG_ERROR() + << filename + << "contains a library with an 'extract' field that's not an object"; + return out; + } + QJsonObject extractObj = libObj.value("extract").toObject(); + if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray()) + { + QLOG_ERROR() << filename + << "contains a library with an invalid 'extract' field"; + return out; + } + out.applyExcludes = true; + QJsonArray excludeArray = extractObj.value("exclude").toArray(); + for (auto excludeVal : excludeArray) + { + if (!excludeVal.isString()) + { + QLOG_WARN() << filename << "contains a library that contains an 'extract' " + "field that contains an invalid 'exclude' entry " + "(skipping)"; + } + else + { + out.excludes.append(excludeVal.toString()); + } + } + } + if (libObj.contains("natives")) + { + if (!libObj.value("natives").isObject()) + { + QLOG_ERROR() + << filename + << "contains a library with a 'natives' field that's not an object"; + return out; + } + out.applyNatives = true; + QJsonObject nativesObj = libObj.value("natives").toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + if (!it.value().isString()) + { + QLOG_WARN() << filename << "contains an invalid native (skipping)"; + } + OpSys opSys = OpSys_fromString(it.key()); + if (opSys != Os_Other) + { + out.natives.append(qMakePair(opSys, it.value().toString())); + } + } + } + if (libObj.contains("rules")) + { + out.applyRules = true; + out.rules = rulesFromJsonV4(libObj); + } + isError = false; + return out; + } + static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, bool &isError) + { + VersionFile out; + isError = true; + if (doc.isEmpty() || doc.isNull()) + { + QLOG_ERROR() << filename << "is empty or null"; + return out; + } + if (!doc.isObject()) + { + QLOG_ERROR() << "The root of" << filename << "is not an object"; + return out; + } + + QJsonObject root = doc.object(); + + if (requireOrder) + { + if (root.contains("order")) + { + if (root.value("order").isDouble()) + { + out.order = root.value("order").toDouble(); + } + else + { + QLOG_ERROR() << "'order' field contains an invalid value in" << filename; + return out; + } + } + else + { + QLOG_ERROR() << filename << "doesn't contain an order field"; + } + } + + auto readString = [root, filename](const QString &key, QString &variable) + { + if (root.contains(key)) + { + QJsonValue val = root.value(key); + if (!val.isString()) + { + QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; + } + else + { + variable = val.toString(); + } + } + }; + + readString("id", out.id); + readString("mainClass", out.mainClass); + readString("processArguments", out.processArguments); + readString("minecraftArguments", out.overwriteMinecraftArguments); + readString("+minecraftArguments", out.addMinecraftArguments); + readString("-minecraftArguments", out.removeMinecraftArguments); + readString("type", out.type); + readString("releaseTime", out.releaseTime); + readString("time", out.time); + readString("assets", out.assets); + if (root.contains("minimumLauncherVersion")) + { + QJsonValue val = root.value("minimumLauncherVersion"); + if (!val.isDouble()) + { + QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename + << "(skipping)"; + } + else + { + out.minimumLauncherVersion = val.toDouble(); + } + } + + if (root.contains("tweakers")) + { + QJsonValue tweakersVal = root.value("tweakers"); + if (!tweakersVal.isArray()) + { + QLOG_ERROR() << filename << "contains a 'tweakers' field, but it's not an array"; + return out; + } + out.shouldOverwriteTweakers = true; + QJsonArray tweakers = root.value("tweakers").toArray(); + for (auto tweakerVal : tweakers) + { + if (!tweakerVal.isString()) + { + QLOG_ERROR() << filename << "contains a 'tweakers' field entry that's not a string"; + return out; + } + out.overwriteTweakers.append(tweakerVal.toString()); + } + } + if (root.contains("+tweakers")) + { + QJsonValue tweakersVal = root.value("+tweakers"); + if (!tweakersVal.isArray()) + { + QLOG_ERROR() << filename << "contains a '+tweakers' field, but it's not an array"; + return out; + } + QJsonArray tweakers = root.value("+tweakers").toArray(); + for (auto tweakerVal : tweakers) + { + if (!tweakerVal.isString()) + { + QLOG_ERROR() << filename << "contains a '+tweakers' field entry that's not a string"; + return out; + } + out.addTweakers.append(tweakerVal.toString()); + } + } + if (root.contains("-tweakers")) + { + QJsonValue tweakersVal = root.value("-tweakers"); + if (!tweakersVal.isArray()) + { + QLOG_ERROR() << filename << "contains a '-tweakers' field, but it's not an array"; + return out; + } + out.shouldOverwriteTweakers = true; + QJsonArray tweakers = root.value("-tweakers").toArray(); + for (auto tweakerVal : tweakers) + { + if (!tweakerVal.isString()) + { + QLOG_ERROR() << filename << "contains a '-tweakers' field entry that's not a string"; + return out; + } + out.removeTweakers.append(tweakerVal.toString()); + } + } + + if (root.contains("libraries")) + { + out.shouldOverwriteLibs = true; + QJsonValue librariesVal = root.value("libraries"); + if (!librariesVal.isArray()) + { + QLOG_ERROR() << filename + << "contains a 'libraries' field, but its not an array"; + return out; + } + QJsonArray librariesArray = librariesVal.toArray(); + for (auto libVal : librariesArray) + { + if (!libVal.isObject()) + { + QLOG_ERROR() << filename << "contains a library that's not an object"; + return out; + } + QJsonObject libObj = libVal.toObject(); + bool error; + Library lib = fromLibraryJson(libObj, filename, error); + if (error) + { + QLOG_ERROR() << "Error while reading a library entry in" << filename; + return out; + } + out.overwriteLibs.append(lib); + } + } + if (root.contains("+libraries")) + { + QJsonValue librariesVal = root.value("+libraries"); + if (!librariesVal.isArray()) + { + QLOG_ERROR() << filename + << "contains a '+libraries' field, but its not an array"; + return out; + } + QJsonArray librariesArray = librariesVal.toArray(); + for (auto libVal : librariesArray) + { + if (!libVal.isObject()) + { + QLOG_ERROR() << filename << "contains a library that's not an object"; + return out; + } + QJsonObject libObj = libVal.toObject(); + bool error; + Library lib = fromLibraryJson(libObj, filename, error); + if (error) + { + QLOG_ERROR() << "Error while reading a library entry in" << filename; + return out; + } + if (!libObj.contains("insert")) + { + QLOG_ERROR() << "Missing 'insert' field in '+libraries' field in" + << filename; + return out; + } + QJsonValue insertVal = libObj.value("insert"); + QString insertString; + { + if (insertVal.isString()) + { + insertString = insertVal.toString(); + } + else if (insertVal.isObject()) + { + QJsonObject insertObj = insertVal.toObject(); + if (insertObj.isEmpty()) + { + QLOG_ERROR() << "One library has an empty insert object in" + << filename; + return out; + } + insertString = insertObj.keys().first(); + lib.insertData = insertObj.value(insertString).toString(); + } + } + if (insertString == "apply") + { + lib.insertType = Library::Apply; + } + else if (insertString == "append") + { + lib.insertType = Library::Append; + } + else if (insertString == "prepend") + { + lib.insertType = Library::Prepend; + } + else if (insertString == "before") + { + lib.insertType = Library::InsertBefore; + } + else if (insertString == "after") + { + lib.insertType = Library::InsertAfter; + } + else if (insertString == "replace") + { + lib.insertType = Library::Replace; + } + else + { + QLOG_ERROR() << "A '+' library in" << filename + << "contains an invalid insert type"; + return out; + } + out.addLibs.append(lib); + } + } + if (root.contains("-libraries")) + { + QJsonValue librariesVal = root.value("-libraries"); + if (!librariesVal.isArray()) + { + QLOG_ERROR() << filename + << "contains a '-libraries' field, but its not an array"; + return out; + } + QJsonArray librariesArray = librariesVal.toArray(); + for (auto libVal : librariesArray) + { + if (!libVal.isObject()) + { + QLOG_ERROR() << filename << "contains a library that's not an object"; + return out; + } + QJsonObject libObj = libVal.toObject(); + if (!libObj.contains("name")) + { + QLOG_ERROR() << filename << "contains a library without a name"; + return out; + } + if (!libObj.value("name").isString()) + { + QLOG_ERROR() << filename + << "contains a library without a valid 'name' field"; + return out; + } + out.removeLibs.append(libObj.value("name").toString()); + } + } + + isError = false; + return out; + } + + static std::shared_ptr createLibrary(const Library &lib) + { + std::shared_ptr out(new OneSixLibrary(lib.name)); + out->setBaseUrl(lib.url); + out->setHint(lib.hint); + out->setAbsoluteUrl(lib.absoluteUrl); + out->extract_excludes = lib.excludes; + for (auto native : lib.natives) + { + out->addNative(native.first, native.second); + } + out->setRules(lib.rules); + out->finalize(); + return out; + } + int findLibrary(QList> haystack, const QString &needle) + { + for (int i = 0; i < haystack.size(); ++i) + { + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix) + .indexIn(haystack.at(i)->rawName()) != -1) + { + return i; + } + } + return -1; + } + void applyTo(OneSixVersion *version, bool &isError) + { + isError = true; + if (!id.isNull()) + { + version->id = id; + } + if (!mainClass.isNull()) + { + version->mainClass = mainClass; + } + if (!processArguments.isNull()) + { + version->processArguments = processArguments; + } + if (!type.isNull()) + { + version->type = type; + } + if (!releaseTime.isNull()) + { + version->releaseTime = releaseTime; + } + if (!time.isNull()) + { + version->time = time; + } + if (!assets.isNull()) + { + version->assets = assets; + } + if (minimumLauncherVersion >= 0) + { + version->minimumLauncherVersion = minimumLauncherVersion; + } + if (!overwriteMinecraftArguments.isNull()) + { + version->minecraftArguments = overwriteMinecraftArguments; + } + if (!addMinecraftArguments.isNull()) + { + version->minecraftArguments += addMinecraftArguments; + } + if (!removeMinecraftArguments.isNull()) + { + version->minecraftArguments.remove(removeMinecraftArguments); + } + if (shouldOverwriteTweakers) + { + version->tweakers = overwriteTweakers; + } + for (auto tweaker : addTweakers) + { + version->tweakers += tweaker; + } + for (auto tweaker : removeTweakers) + { + version->tweakers.removeAll(tweaker); + } + if (shouldOverwriteLibs) + { + version->libraries.clear(); + for (auto lib : overwriteLibs) + { + version->libraries.append(createLibrary(lib)); + } + } + for (auto lib : addLibs) + { + switch (lib.insertType) + { + case Library::Apply: + { + + int index = findLibrary(version->libraries, lib.name); + if (index >= 0) + { + auto library = version->libraries[index]; + if (!lib.url.isNull()) + { + library->setBaseUrl(lib.url); + } + if (!lib.hint.isNull()) + { + library->setHint(lib.hint); + } + if (!lib.absoluteUrl.isNull()) + { + library->setAbsoluteUrl(lib.absoluteUrl); + } + if (lib.applyExcludes) + { + library->extract_excludes = lib.excludes; + } + if (lib.applyNatives) + { + library->clearSuffixes(); + for (auto native : lib.natives) + { + library->addNative(native.first, native.second); + } + } + if (lib.applyRules) + { + library->setRules(lib.rules); + } + library->finalize(); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + case Library::Append: + version->libraries.append(createLibrary(lib)); + break; + case Library::Prepend: + version->libraries.prepend(createLibrary(lib)); + break; + case Library::InsertBefore: + { + + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.insert(index, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + case Library::InsertAfter: + { + + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.insert(index + 1, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + case Library::Replace: + { + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.replace(index, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + } + } + for (auto lib : removeLibs) + { + int index = findLibrary(version->libraries, lib); + if (index >= 0) + { + version->libraries.removeAt(index); + } + else + { + QLOG_WARN() << "Couldn't find" << lib << "(skipping)"; + } + } + + isError = false; + } +}; + OneSixVersionBuilder::OneSixVersionBuilder() { - } -bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent) +bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, + QWidget *widgetParent, const bool excludeCustom) { OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = instance; builder.m_widgetParent = widgetParent; - return builder.build(); + return builder.build(excludeCustom); } bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) @@ -54,7 +696,7 @@ bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) return builder.read(obj); } -bool OneSixVersionBuilder::build() +bool OneSixVersionBuilder::build(const bool excludeCustom) { m_version->clear(); @@ -66,13 +708,20 @@ bool OneSixVersionBuilder::build() // version.json { QLOG_INFO() << "Reading version.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) { return false; } - if (!apply(obj)) + bool isError = false; + file.applyTo(m_version, isError); + if (isError) { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("version.json"))); return false; } } @@ -81,49 +730,87 @@ bool OneSixVersionBuilder::build() { // load all, put into map for ordering, apply in the right order - QMap objects; + QMap> files; for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { QLOG_INFO() << "Reading" << info.fileName(); - QJsonObject obj; - if (!read(info, &obj)) + VersionFile file; + if (!read(info, true, &file)) { return false; } - if (!obj.contains("order") || !obj.value("order").isDouble()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); - return false; - } - objects.insert(obj.value("order").toDouble(), obj); + files.insert(file.order, qMakePair(info.fileName(), file)); } - for (auto object : objects.values()) + for (auto order : files.keys()) { - qDebug() << "Applying object with order" << objects.key(object); - if (!apply(object)) + QLOG_DEBUG() << "Applying file with order" << order; + auto filePair = files[order]; + bool isError = false; + filePair.second.applyTo(m_version, isError); + if (isError) { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(filePair.first)); return false; } } } // custom.json + if (!excludeCustom) { if (QFile::exists(root.absoluteFilePath("custom.json"))) { QLOG_INFO() << "Reading custom.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) { return false; } - if (!apply(obj)) + bool isError = false; + file.applyTo(m_version, isError); + if (isError) { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("custom.json"))); return false; } } } + // some final touches + { + if (m_version->assets.isEmpty()) + { + m_version->assets = "legacy"; + } + if (m_version->minecraftArguments.isEmpty()) + { + QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") + { + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; + } + else if (toCompare == "username_session") + { + m_version->minecraftArguments = + "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + m_version->minecraftArguments = "--username ${auth_player_name} " + "--session ${auth_session} " + "--version ${profile_name}"; + } + } + } + return true; } @@ -131,316 +818,57 @@ bool OneSixVersionBuilder::read(const QJsonObject &obj) { m_version->clear(); - return apply(obj); -} - -void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) -{ - if (obj.contains(key) && obj.value(key).isString()) + bool isError = false; + VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false, isError); + if (isError) { - out = obj.value(key).toString(); + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while reading. Please check MultiMC-0.log for more info.")); + return false; } - else if (!onlyOverride) + file.applyTo(m_version, isError); + if (isError) { - if (obj.contains("+" + key) && obj.value("+" + key).isString()) - { - out += obj.value("+" + key).toString(); - } - else if (obj.contains("-" + key) && obj.value("-" + key).isString()) - { - out.remove(obj.value("-" + key).toString()); - } - } -} -void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(OneSixLibrary::*func)(const QString &val)) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - (lib.get()->*func)(obj.value(key).toString()); - } -} -bool OneSixVersionBuilder::apply(const QJsonObject &object) -{ - applyString(object, "id", m_version->id); - applyString(object, "mainClass", m_version->mainClass); - applyString(object, "minecraftArguments", m_version->minecraftArguments, false); - applyString(object, "processArguments", m_version->processArguments, false); - if (m_version->minecraftArguments.isEmpty()) - { - const QString toCompare = m_version->processArguments.toLower(); - if (toCompare == "legacy") - { - m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } - } - applyString(object, "type", m_version->type); - applyString(object, "releaseTime", m_version->releaseTime); - applyString(object, "time", m_version->time); - applyString(object, "assets", m_version->assets); - { - if (m_version->assets.isEmpty()) - { - m_version->assets = "legacy"; - } - } - if (object.contains("minimumLauncherVersion")) - { - auto minLauncherVersionVal = object.value("minimumLauncherVersion"); - if (minLauncherVersionVal.isDouble()) - { - m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); - } - } - - // libraries - if (object.contains("libraries")) - { - m_version->libraries.clear(); - auto librariesValue = object.value("libraries"); - if (!librariesValue.isArray()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; - } - auto array = librariesValue.toArray(); - for (auto libVal : array) - { - if (libVal.isObject()) - { - if (!applyLibrary(libVal.toObject(), Override)) - { - return false; - } - } - - } - } - - // +libraries - if (object.contains("+libraries")) - { - auto librariesValue = object.value("+libraries"); - if (!librariesValue.isArray()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; - } - for (auto libVal : librariesValue.toArray()) - { - if (libVal.isObject()) - { - applyLibrary(libVal.toObject(), Add); - } - - } - } - - // -libraries - if (object.contains("-libraries")) - { - auto librariesValue = object.value("-libraries"); - if (!librariesValue.isArray()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; - } - for (auto libVal : librariesValue.toArray()) - { - if (libVal.isObject()) - { - applyLibrary(libVal.toObject(), Remove); - } - - } + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while applying. Please check MultiMC-0.log for more info.")); + return false; } return true; } -int findLibrary(QList > haystack, const QString &needle) -{ - for (int i = 0; i < haystack.size(); ++i) - { - if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(haystack.at(i)->rawName()) != -1) - { - return i; - } - } - return -1; -} - -bool OneSixVersionBuilder::applyLibrary(const QJsonObject &lib, const OneSixVersionBuilder::Type type) -{ - // Library name - auto nameVal = lib.value("name"); - if (!nameVal.isString()) - { - return false; - } - auto name = nameVal.toString(); - - if (type == Remove) - { - int index = findLibrary(m_version->libraries, name); - if (index >= 0) - { - m_version->libraries.removeAt(index); - } - return true; - } - - if (type == Add && !lib.contains("insert")) - { - return false; - } - - std::shared_ptr library; - - if (lib.value("insert").toString() != "apply" && type == Add) - { - QMutableListIterator > it(m_version->libraries); - while (it.hasNext()) - { - if (it.next()->rawName() == name) - { - it.remove(); - } - } - } - - if (lib.value("insert").toString() == "apply" && type == Add) - { - library = m_version->libraries[findLibrary(m_version->libraries, name)]; - } - else - { - library.reset(new OneSixLibrary(nameVal.toString())); - } - - applyString(lib, "url", library, &OneSixLibrary::setBaseUrl); - applyString(lib, "MMC-hint", library, &OneSixLibrary::setHint); - applyString(lib, "MMC-absulute_url", library, &OneSixLibrary::setAbsoluteUrl); - applyString(lib, "MMC-absoluteUrl", library, &OneSixLibrary::setAbsoluteUrl); - - auto extractVal = lib.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) - { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) - { - if (excludeVal.isString()) - { - excludes.append(excludeVal.toString()); - } - } - library->extract_excludes = excludes; - } - } - - auto nativesVal = lib.value("natives"); - if (nativesVal.isObject()) - { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - auto osType = OpSys_fromString(it.key()); - if (osType == Os_Other) - { - continue; - } - if (!it.value().isString()) - { - continue; - } - library->addNative(osType, it.value().toString()); - } - } - - if (lib.contains("rules")) - { - library->setRules(rulesFromJsonV4(lib)); - } - library->finalize(); - if (type == Override) - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() != "apply") - { - if (lib.value("insert").toString() == "append") - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() == "prepend") - { - m_version->libraries.prepend(library); - } - else if (lib.value("insert").isObject()) - { - QJsonObject insertObj = lib.value("insert").toObject(); - if (insertObj.isEmpty()) - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); - return false; - } - const QString key = insertObj.keys().first(); - const QString value = insertObj.value(key).toString(); - const int index = findLibrary(m_version->libraries, value); - if (index >= 0) - { - if (key == "before") - { - m_version->libraries.insert(index, library); - } - else if (key == "after") - { - m_version->libraries.insert(index + 1, library); - } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } - } - } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } - } - return true; -} - -bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder, + VersionFile *out) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); return false; } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); if (error.error != QJsonParseError::NoError) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Unable to parse %1: %2 at %3").arg(file.fileName(), error.errorString()).arg(error.offset)); + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), + QObject::tr("Unable to parse %1: %2 at %3") + .arg(file.fileName(), error.errorString()) + .arg(error.offset)); return false; } - *out = doc.object(); + bool isError = false; + *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.") + .arg(file.fileName())); + ; + } return true; } diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index a3ba331c..ac8c13bf 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -22,12 +22,13 @@ class OneSixInstance; class QWidget; class QJsonObject; class QFileInfo; +class VersionFile; class OneSixVersionBuilder { OneSixVersionBuilder(); public: - static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent); + static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool excludeCustom); static bool read(OneSixVersion *version, const QJsonObject &obj); private: @@ -35,19 +36,8 @@ private: OneSixInstance *m_instance; QWidget *m_widgetParent; - enum Type - { - Override, - Add, - Remove - }; - - bool build(); + bool build(const bool excludeCustom); bool read(const QJsonObject &obj); - void clear(); - bool apply(const QJsonObject &object); - bool applyLibrary(const QJsonObject &lib, const Type type); - - bool read(const QFileInfo &fileInfo, QJsonObject *out); + bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out); }; From 176783c8caacafd5d320c2fef7374335ee54796b Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 20:17:29 +0100 Subject: [PATCH 21/49] Have the libraries tab show tweaker mods instead of libraries --- gui/dialogs/OneSixModEditDialog.ui | 10 ++++++ logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixVersion.cpp | 52 +++++++----------------------- logic/OneSixVersion.h | 2 -- 4 files changed, 23 insertions(+), 43 deletions(-) diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui index d57d0f85..67642c23 100644 --- a/gui/dialogs/OneSixModEditDialog.ui +++ b/gui/dialogs/OneSixModEditDialog.ui @@ -35,6 +35,13 @@ + + + + Tweakers: + + + @@ -43,6 +50,9 @@ Qt::ScrollBarAlwaysOff + + false + diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 1ce9d3f4..1c38fd5e 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -53,7 +53,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) QJsonObject obj; obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch")); - obj.insert("+minecraftArguments", QString(" --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker")); + obj.insert("+tweakers", QJsonArray::fromStringList(QStringList() << "com.mumfrey.liteloader.launch.LiteLoaderTweaker")); obj.insert("order", 10); QJsonArray libraries; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 1b6bc9cb..1abd5114 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -27,11 +27,15 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom) { - return OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); + beginResetModel(); + bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); + endResetModel(); + return ret; } void OneSixVersion::clear() { + beginResetModel(); id.clear(); time.clear(); releaseTime.clear(); @@ -43,6 +47,7 @@ void OneSixVersion::clear() mainClass.clear(); libraries.clear(); tweakers.clear(); + endResetModel(); } void OneSixVersion::dump() const @@ -109,21 +114,14 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const int row = index.row(); int column = index.column(); - if (row < 0 || row >= libraries.size()) + if (row < 0 || row >= tweakers.size()) return QVariant(); if (role == Qt::DisplayRole) { - switch (column) + if (column == 0) { - case 0: - return libraries[row]->name(); - case 1: - return libraries[row]->type(); - case 2: - return libraries[row]->version(); - default: - return QVariant(); + return tweakers.at(row); } } return QVariant(); @@ -133,43 +131,17 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; - int row = index.row(); - if (libraries[row]->isActive()) - { - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; - } - else - { - return Qt::ItemNeverHasChildren; - } - // return QAbstractListModel::flags(index); -} - -QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - switch (section) - { - case 0: - return QString("Name"); - case 1: - return QString("Type"); - case 2: - return QString("Version"); - default: - return QString(); - } + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } int OneSixVersion::rowCount(const QModelIndex &parent) const { - return libraries.size(); + return tweakers.size(); } int OneSixVersion::columnCount(const QModelIndex &parent) const { - return 3; + return 1; } QDebug operator<<(QDebug &dbg, const OneSixVersion *version) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 767e05db..98a4b418 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -33,8 +33,6 @@ public: virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; From 986141b5035c99747c7e06cd447b8b4e27eb2b27 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 22:23:07 +0100 Subject: [PATCH 22/49] Fix library ordering --- logic/ForgeInstaller.cpp | 8 ++------ logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixVersionBuilder.cpp | 36 +++++++++++++--------------------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index c0340e95..8d2445ed 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -160,7 +160,7 @@ bool ForgeInstaller::add(OneSixInstance *to) equals = true; } // replace lib - libObj.insert("insert", QString("apply")); + libObj.insert("insert", QString("replace")); break; } if (equals) @@ -170,11 +170,7 @@ bool ForgeInstaller::add(OneSixInstance *to) if (!found) { // add lib - QJsonObject insertObj; - insertObj.insert( - "before", - to->getFullVersion()->libraries.at(sliding_insert_window + 1)->rawName()); - libObj.insert("insert", insertObj); + libObj.insert("insert", QString("prepend-if-not-exists")); sliding_insert_window++; } librariesPlus.prepend(libObj); diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 1c38fd5e..f658f834 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -63,7 +63,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); - lwLibObj.insert("insert", QString("prepend")); + lwLibObj.insert("insert", QString("prepend-if-not-exists")); libraries.append(lwLibObj); } diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index c031a94a..3b119b45 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -70,8 +70,8 @@ struct VersionFile Apply, Append, Prepend, - InsertBefore, - InsertAfter, + AppendIfNotExists, + PrependIfNotExists, Replace }; InsertType insertType; @@ -408,13 +408,13 @@ struct VersionFile { lib.insertType = Library::Prepend; } - else if (insertString == "before") + else if (insertString == "prepend-if-not-exists") { - lib.insertType = Library::InsertBefore; + lib.insertType = Library::PrependIfNotExists; } - else if (insertString == "after") + else if (insertString == "append-if-not-exists") { - lib.insertType = Library::InsertAfter; + lib.insertType = Library::PrependIfNotExists; } else if (insertString == "replace") { @@ -613,31 +613,23 @@ struct VersionFile case Library::Prepend: version->libraries.prepend(createLibrary(lib)); break; - case Library::InsertBefore: + case Library::AppendIfNotExists: { - int index = findLibrary(version->libraries, lib.insertData); - if (index >= 0) + int index = findLibrary(version->libraries, lib.name); + if (index < 0) { - version->libraries.insert(index, createLibrary(lib)); - } - else - { - QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + version->libraries.append(createLibrary(lib)); } break; } - case Library::InsertAfter: + case Library::PrependIfNotExists: { - int index = findLibrary(version->libraries, lib.insertData); - if (index >= 0) + int index = findLibrary(version->libraries, lib.name); + if (index < 0) { - version->libraries.insert(index + 1, createLibrary(lib)); - } - else - { - QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + version->libraries.prepend(createLibrary(lib)); } break; } From 556d8f0ec16e5030cfbfac9dc0fc2c796e829dcb Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Tue, 28 Jan 2014 07:39:43 +0100 Subject: [PATCH 23/49] custom.json overrides all. For user patching there now is instance.json --- logic/OneSixVersionBuilder.cpp | 119 ++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 48 deletions(-) diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 3b119b45..a8e7253b 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -695,13 +695,11 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) QDir root(m_instance->instanceRoot()); QDir patches(root.absoluteFilePath("patches/")); - // version.json -> patches/*.json -> custom.json - - // version.json + if (QFile::exists(root.absoluteFilePath("custom.json"))) { - QLOG_INFO() << "Reading version.json"; + QLOG_INFO() << "Reading custom.json"; VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) { return false; } @@ -713,52 +711,19 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) m_widgetParent, QObject::tr("Error"), QObject::tr( "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("version.json"))); + .arg(root.absoluteFilePath("custom.json"))); return false; } } - - // patches/ + else { - // load all, put into map for ordering, apply in the right order + // version.json -> patches/*.json -> instance.json - QMap> files; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + // version.json { - QLOG_INFO() << "Reading" << info.fileName(); + QLOG_INFO() << "Reading version.json"; VersionFile file; - if (!read(info, true, &file)) - { - return false; - } - files.insert(file.order, qMakePair(info.fileName(), file)); - } - for (auto order : files.keys()) - { - QLOG_DEBUG() << "Applying file with order" << order; - auto filePair = files[order]; - bool isError = false; - filePair.second.applyTo(m_version, isError); - if (isError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(filePair.first)); - return false; - } - } - } - - // custom.json - if (!excludeCustom) - { - if (QFile::exists(root.absoluteFilePath("custom.json"))) - { - QLOG_INFO() << "Reading custom.json"; - VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) + if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) { return false; } @@ -767,13 +732,71 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) if (isError) { QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("custom.json"))); + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("version.json"))); return false; } } + + // patches/ + { + // load all, put into map for ordering, apply in the right order + + QMap> files; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + { + QLOG_INFO() << "Reading" << info.fileName(); + VersionFile file; + if (!read(info, true, &file)) + { + return false; + } + files.insert(file.order, qMakePair(info.fileName(), file)); + } + for (auto order : files.keys()) + { + QLOG_DEBUG() << "Applying file with order" << order; + auto filePair = files[order]; + bool isError = false; + filePair.second.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(filePair.first)); + return false; + } + } + } + + // instance.json + if (!excludeCustom) + { + if (QFile::exists(root.absoluteFilePath("instance.json"))) + { + QLOG_INFO() << "Reading instance.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("instance.json")), false, &file)) + { + return false; + } + bool isError = false; + file.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("instance.json"))); + return false; + } + } + } } // some final touches From 3fb7a0faf069fdc5a4f14a12dfd76c43819c3378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 31 Jan 2014 22:51:45 +0100 Subject: [PATCH 24/49] Reformat, Rename, Redo --- .clang-format | 24 ++ CMakeLists.txt | 20 +- CategorizedProxyModel.h | 18 -- CategorizedViewCategory.cpp => Group.cpp | 51 ++-- CategorizedViewCategory.h => Group.h | 15 +- CategorizedView.cpp => GroupView.cpp | 277 ++++++++++-------- CategorizedView.h => GroupView.h | 44 +-- ...zedProxyModel.cpp => GroupedProxyModel.cpp | 10 +- GroupedProxyModel.h | 14 + InstanceDelegate.cpp | 8 +- InstanceDelegate.h | 8 +- main.cpp | 25 +- main.h | 12 +- 13 files changed, 297 insertions(+), 229 deletions(-) create mode 100644 .clang-format delete mode 100644 CategorizedProxyModel.h rename CategorizedViewCategory.cpp => Group.cpp (64%) rename CategorizedViewCategory.h => Group.h (63%) rename CategorizedView.cpp => GroupView.cpp (68%) rename CategorizedView.h => GroupView.h (68%) rename CategorizedProxyModel.cpp => GroupedProxyModel.cpp (55%) create mode 100644 GroupedProxyModel.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..167a8fa7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +UseTab: true +IndentWidth: 4 +TabWidth: 4 +ConstructorInitializerIndentWidth: 4 +AccessModifierOffset: -4 +IndentCaseLabels: false +IndentFunctionDeclarationAfterType: false +NamespaceIndentation: None + +BreakBeforeBraces: Allman +AllowShortIfStatementsOnASingleLine: false +ColumnLimit: 96 +MaxEmptyLinesToKeep: 1 + +Standard: Cpp11 +Cpp11BracedListStyle: true + +SpacesInParentheses: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true + +AlignTrailingComments: true +SpacesBeforeTrailingComments: 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 44a28c57..e2a85950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,17 +23,17 @@ find_package(Qt5Widgets REQUIRED) include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS}) set(SOURCES - main.cpp - main.h + main.cpp + main.h - CategorizedView.h - CategorizedView.cpp - CategorizedViewCategory.h - CategorizedViewCategory.cpp - CategorizedProxyModel.h - CategorizedProxyModel.cpp - InstanceDelegate.h - InstanceDelegate.cpp + GroupView.h + GroupView.cpp + Group.h + Group.cpp + GroupedProxyModel.h + GroupedProxyModel.cpp + InstanceDelegate.h + InstanceDelegate.cpp ) add_executable(GroupView ${SOURCES}) diff --git a/CategorizedProxyModel.h b/CategorizedProxyModel.h deleted file mode 100644 index 6e4f3fdc..00000000 --- a/CategorizedProxyModel.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef CATEGORIZEDPROXYMODEL_H -#define CATEGORIZEDPROXYMODEL_H - -#include - -class CategorizedProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - CategorizedProxyModel(QObject *parent = 0); - -protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; -}; - - -#endif // CATEGORIZEDPROXYMODEL_H diff --git a/CategorizedViewCategory.cpp b/Group.cpp similarity index 64% rename from CategorizedViewCategory.cpp rename to Group.cpp index b82ffc96..f23066c5 100644 --- a/CategorizedViewCategory.cpp +++ b/Group.cpp @@ -1,32 +1,34 @@ -#include "CategorizedViewCategory.h" +#include "Group.h" #include #include #include -#include "CategorizedView.h" +#include "GroupView.h" -CategorizedViewCategory::CategorizedViewCategory(const QString &text, CategorizedView *view) +Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) { } -CategorizedViewCategory::CategorizedViewCategory(const CategorizedViewCategory *other) : - view(other->view), text(other->text), collapsed(other->collapsed), iconRect(other->iconRect), textRect(other->textRect) +Group::Group(const Group *other) + : view(other->view), text(other->text), collapsed(other->collapsed), + iconRect(other->iconRect), textRect(other->textRect) { } -void CategorizedViewCategory::update() +void Group::update() { firstRow = firstItem().row(); rowHeights = QVector(numRows()); for (int i = 0; i < numRows(); ++i) { - rowHeights[i] = view->categoryRowHeight(view->model()->index(i * view->itemsPerRow() + firstRow, 0)); + rowHeights[i] = view->categoryRowHeight( + view->model()->index(i * view->itemsPerRow() + firstRow, 0)); } } -void CategorizedViewCategory::drawHeader(QPainter *painter, const int y) +void Group::drawHeader(QPainter *painter, const int y) { painter->save(); @@ -51,23 +53,27 @@ void CategorizedViewCategory::drawHeader(QPainter *painter, const int y) int textWidth = painter->fontMetrics().width(text); textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); painter->setBrush(view->viewOptions().palette.text()); - view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, view->viewport()->palette(), true, text); + view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, + view->viewport()->palette(), true, text); // the line - painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); + painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, + view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); painter->restore(); } -int CategorizedViewCategory::totalHeight() const +int Group::totalHeight() const { return headerHeight() + 5 + contentHeight(); } -int CategorizedViewCategory::headerHeight() const + +int Group::headerHeight() const { return view->viewport()->fontMetrics().height() + 4; } -int CategorizedViewCategory::contentHeight() const + +int Group::contentHeight() const { if (collapsed) { @@ -80,14 +86,16 @@ int CategorizedViewCategory::contentHeight() const } return result; } -int CategorizedViewCategory::numRows() const + +int Group::numRows() const { return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow())); } -int CategorizedViewCategory::top() const + +int Group::top() const { int res = 0; - const QList cats = view->m_categories; + const QList cats = view->m_categories; for (int i = 0; i < cats.size(); ++i) { if (cats.at(i) == this) @@ -99,7 +107,7 @@ int CategorizedViewCategory::top() const return res; } -QList CategorizedViewCategory::items() const +QList Group::items() const { QList indices; for (int i = 0; i < view->model()->rowCount(); ++i) @@ -112,16 +120,19 @@ QList CategorizedViewCategory::items() const } return indices; } -int CategorizedViewCategory::numItems() const + +int Group::numItems() const { return items().size(); } -QModelIndex CategorizedViewCategory::firstItem() const + +QModelIndex Group::firstItem() const { QList indices = items(); return indices.isEmpty() ? QModelIndex() : indices.first(); } -QModelIndex CategorizedViewCategory::lastItem() const + +QModelIndex Group::lastItem() const { QList indices = items(); return indices.isEmpty() ? QModelIndex() : indices.last(); diff --git a/CategorizedViewCategory.h b/Group.h similarity index 63% rename from CategorizedViewCategory.h rename to Group.h index cb6ef8c5..6a8fadeb 100644 --- a/CategorizedViewCategory.h +++ b/Group.h @@ -1,19 +1,18 @@ -#ifndef CATEGORIZEDVIEWROW_H -#define CATEGORIZEDVIEWROW_H +#pragma once #include #include #include -class CategorizedView; +class GroupView; class QPainter; class QModelIndex; -struct CategorizedViewCategory +struct Group { - CategorizedViewCategory(const QString &text, CategorizedView *view); - CategorizedViewCategory(const CategorizedViewCategory *other); - CategorizedView *view; + Group(const QString &text, GroupView *view); + Group(const Group *other); + GroupView *view; QString text; bool collapsed; QRect iconRect; @@ -35,5 +34,3 @@ struct CategorizedViewCategory QModelIndex firstItem() const; QModelIndex lastItem() const; }; - -#endif // CATEGORIZEDVIEWROW_H diff --git a/CategorizedView.cpp b/GroupView.cpp similarity index 68% rename from CategorizedView.cpp rename to GroupView.cpp index 60230661..e3bc1055 100644 --- a/CategorizedView.cpp +++ b/GroupView.cpp @@ -1,4 +1,4 @@ -#include "CategorizedView.h" +#include "GroupView.h" #include #include @@ -11,12 +11,11 @@ #include #include -#include "CategorizedViewCategory.h" +#include "Group.h" -template -bool listsIntersect(const QList &l1, const QList t2) +template bool listsIntersect(const QList &l1, const QList t2) { - foreach (const T &item, l1) + for (auto &item : l1) { if (t2.contains(item)) { @@ -26,31 +25,33 @@ bool listsIntersect(const QList &l1, const QList t2) return false; } -CategorizedView::CategorizedView(QWidget *parent) - : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), m_categoryMargin(5)//, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) +GroupView::GroupView(QWidget *parent) + : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), + m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) { setViewMode(IconMode); - setMovement(Snap); + //setMovement(Snap); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setWordWrap(true); - setDragDropMode(QListView::InternalMove); + //setDragDropMode(QListView::InternalMove); setAcceptDrops(true); setSpacing(10); } -CategorizedView::~CategorizedView() +GroupView::~GroupView() { qDeleteAll(m_categories); m_categories.clear(); } -void CategorizedView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QVector &roles) { -// if (m_updatesDisabled) -// { -// return; -// } + // if (m_updatesDisabled) + // { + // return; + // } QListView::dataChanged(topLeft, bottomRight, roles); @@ -60,24 +61,25 @@ void CategorizedView::dataChanged(const QModelIndex &topLeft, const QModelIndex update(); } } -void CategorizedView::rowsInserted(const QModelIndex &parent, int start, int end) +void GroupView::rowsInserted(const QModelIndex &parent, int start, int end) { -// if (m_updatesDisabled) -// { -// return; -// } + // if (m_updatesDisabled) + // { + // return; + // } QListView::rowsInserted(parent, start, end); updateGeometries(); update(); } -void CategorizedView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) + +void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { -// if (m_updatesDisabled) -// { -// return; -// } + // if (m_updatesDisabled) + // { + // return; + // } QListView::rowsAboutToBeRemoved(parent, start, end); @@ -85,27 +87,28 @@ void CategorizedView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, update(); } -void CategorizedView::updateGeometries() +void GroupView::updateGeometries() { QListView::updateGeometries(); int previousScroll = verticalScrollBar()->value(); - QMap cats; + QMap cats; for (int i = 0; i < model()->rowCount(); ++i) { - const QString category = model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString(); + const QString category = + model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString(); if (!cats.contains(category)) { - CategorizedViewCategory *old = this->category(category); + Group *old = this->category(category); if (old) { - cats.insert(category, new CategorizedViewCategory(old)); + cats.insert(category, new Group(old)); } else { - cats.insert(category, new CategorizedViewCategory(category, this)); + cats.insert(category, new Group(category, this)); } } } @@ -130,14 +133,14 @@ void CategorizedView::updateGeometries() else { int totalHeight = 0; - foreach (const CategorizedViewCategory *category, m_categories) + for (auto category : m_categories) { totalHeight += category->totalHeight() + m_categoryMargin; } // remove the last margin (we don't want it) totalHeight -= m_categoryMargin; totalHeight += m_bottomMargin; - verticalScrollBar()->setRange(0, totalHeight- height()); + verticalScrollBar()->setRange(0, totalHeight - height()); } verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum())); @@ -145,9 +148,9 @@ void CategorizedView::updateGeometries() update(); } -bool CategorizedView::isIndexHidden(const QModelIndex &index) const +bool GroupView::isIndexHidden(const QModelIndex &index) const { - CategorizedViewCategory *cat = category(index); + Group *cat = category(index); if (cat) { return cat->collapsed; @@ -158,11 +161,12 @@ bool CategorizedView::isIndexHidden(const QModelIndex &index) const } } -CategorizedViewCategory *CategorizedView::category(const QModelIndex &index) const +Group *GroupView::category(const QModelIndex &index) const { return category(index.data(CategorizedViewRoles::CategoryRole).toString()); } -CategorizedViewCategory *CategorizedView::category(const QString &cat) const + +Group *GroupView::category(const QString &cat) const { for (int i = 0; i < m_categories.size(); ++i) { @@ -173,7 +177,8 @@ CategorizedViewCategory *CategorizedView::category(const QString &cat) const } return 0; } -CategorizedViewCategory *CategorizedView::categoryAt(const QPoint &pos) const + +Group *GroupView::categoryAt(const QPoint &pos) const { for (int i = 0; i < m_categories.size(); ++i) { @@ -185,25 +190,28 @@ CategorizedViewCategory *CategorizedView::categoryAt(const QPoint &pos) const return 0; } -int CategorizedView::itemsPerRow() const +int GroupView::itemsPerRow() const { - return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + spacing())); + return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + /* spacing */ 10)); } -int CategorizedView::contentWidth() const + +int GroupView::contentWidth() const { return width() - m_leftMargin - m_rightMargin; } -int CategorizedView::itemWidth() const +int GroupView::itemWidth() const { - return itemDelegate()->sizeHint(viewOptions(), model()->index(model()->rowCount() -1, 0)).width(); + return itemDelegate() + ->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0)) + .width(); } -int CategorizedView::categoryRowHeight(const QModelIndex &index) const +int GroupView::categoryRowHeight(const QModelIndex &index) const { QModelIndexList indices; int internalRow = categoryInternalPosition(index).second; - foreach (const QModelIndex &i, category(index)->items()) + for (auto &i : category(index)->items()) { if (categoryInternalPosition(i).second == internalRow) { @@ -212,14 +220,15 @@ int CategorizedView::categoryRowHeight(const QModelIndex &index) const } int largestHeight = 0; - foreach (const QModelIndex &i, indices) + for (auto &i : indices) { - largestHeight = qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); + largestHeight = + qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); } return largestHeight; } -QPair CategorizedView::categoryInternalPosition(const QModelIndex &index) const +QPair GroupView::categoryInternalPosition(const QModelIndex &index) const { QList indices = category(index)->items(); int x = 0; @@ -240,9 +249,10 @@ QPair CategorizedView::categoryInternalPosition(const QModelIndex &ind } return qMakePair(x, y); } -int CategorizedView::categoryInternalRowTop(const QModelIndex &index) const + +int GroupView::categoryInternalRowTop(const QModelIndex &index) const { - CategorizedViewCategory *cat = category(index); + Group *cat = category(index); int categoryInternalRow = categoryInternalPosition(index).second; int result = 0; for (int i = 0; i < categoryInternalRow; ++i) @@ -251,9 +261,11 @@ int CategorizedView::categoryInternalRowTop(const QModelIndex &index) const } return result; } -int CategorizedView::itemHeightForCategoryRow(const CategorizedViewCategory *category, const int internalRow) const + +int GroupView::itemHeightForCategoryRow(const Group *category, + const int internalRow) const { - foreach (const QModelIndex &i, category->items()) + for (auto &i : category->items()) { QPair pos = categoryInternalPosition(i); if (pos.second == internalRow) @@ -264,9 +276,9 @@ int CategorizedView::itemHeightForCategoryRow(const CategorizedViewCategory *cat return -1; } -void CategorizedView::mousePressEvent(QMouseEvent *event) +void GroupView::mousePressEvent(QMouseEvent *event) { - //endCategoryEditor(); + // endCategoryEditor(); QPoint pos = event->pos() + offset(); QPersistentModelIndex index = indexAt(pos); @@ -297,13 +309,15 @@ void CategorizedView::mousePressEvent(QMouseEvent *event) // signal handlers may change the model emit pressed(index); - - } else { + } + else + { // Forces a finalize() even if mouse is pressed, but not on a item selectionModel()->select(QModelIndex(), QItemSelectionModel::Select); } } -void CategorizedView::mouseMoveEvent(QMouseEvent *event) + +void GroupView::mouseMoveEvent(QMouseEvent *event) { QPoint topLeft; QPoint bottomRight = event->pos(); @@ -337,10 +351,8 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) topLeft = bottomRight; } - if (m_pressedIndex.isValid() - && (state() != DragSelectingState) - && (event->buttons() != Qt::NoButton) - && !selectedIndexes().isEmpty()) + if (m_pressedIndex.isValid() && (state() != DragSelectingState) && + (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty()) { setState(DraggingState); return; @@ -350,7 +362,8 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) { setState(DragSelectingState); QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); - if (m_ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && command.testFlag(QItemSelectionModel::Toggle)) + if (m_ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && + command.testFlag(QItemSelectionModel::Toggle)) { command &= ~QItemSelectionModel::Toggle; command |= m_ctrlDragSelectionFlag; @@ -361,20 +374,21 @@ void CategorizedView::mouseMoveEvent(QMouseEvent *event) setSelection(selectionRect, command); // set at the end because it might scroll the view - if (index.isValid() - && (index != selectionModel()->currentIndex()) - && (index.flags() & Qt::ItemIsEnabled)) + if (index.isValid() && (index != selectionModel()->currentIndex()) && + (index.flags() & Qt::ItemIsEnabled)) { selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); } } } -void CategorizedView::mouseReleaseEvent(QMouseEvent *event) + +void GroupView::mouseReleaseEvent(QMouseEvent *event) { QPoint pos = event->pos() + offset(); QPersistentModelIndex index = indexAt(pos); - bool click = (index == m_pressedIndex && index.isValid()) || (m_pressedCategory && m_pressedCategory == categoryAt(pos)); + bool click = (index == m_pressedIndex && index.isValid()) || + (m_pressedCategory && m_pressedCategory == categoryAt(pos)); if (click && m_pressedCategory) { @@ -411,23 +425,22 @@ void CategorizedView::mouseReleaseEvent(QMouseEvent *event) { option.state |= QStyle::State_Selected; } - if ((model()->flags(index) & Qt::ItemIsEnabled) - && style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) + if ((model()->flags(index) & Qt::ItemIsEnabled) && + style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) { emit activated(index); } } } -void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) + +void GroupView::mouseDoubleClickEvent(QMouseEvent *event) { QModelIndex index = indexAt(event->pos()); - if (!index.isValid() - || !(index.flags() & Qt::ItemIsEnabled) - || (m_pressedIndex != index)) + if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index)) { - QMouseEvent me(QEvent::MouseButtonPress, - event->localPos(), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); + QMouseEvent me(QEvent::MouseButtonPress, event->localPos(), event->windowPos(), + event->screenPos(), event->button(), event->buttons(), + event->modifiers()); mousePressEvent(&me); return; } @@ -435,7 +448,8 @@ void CategorizedView::mouseDoubleClickEvent(QMouseEvent *event) QPersistentModelIndex persistent = index; emit doubleClicked(persistent); } -void CategorizedView::paintEvent(QPaintEvent *event) + +void GroupView::paintEvent(QPaintEvent *event) { QPainter painter(this->viewport()); painter.translate(-offset()); @@ -443,7 +457,7 @@ void CategorizedView::paintEvent(QPaintEvent *event) int y = 0; for (int i = 0; i < m_categories.size(); ++i) { - CategorizedViewCategory *category = m_categories.at(i); + Group *category = m_categories.at(i); category->drawHeader(&painter, y); y += category->totalHeight() + m_categoryMargin; } @@ -459,10 +473,11 @@ void CategorizedView::paintEvent(QPaintEvent *event) QStyleOptionViewItemV4 option(viewOptions()); option.rect = visualRect(index); option.widget = this; - option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText : QStyleOptionViewItemV2::None; + option.features |= QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway? if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) { - option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected : QStyle::State_None; + option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected + : QStyle::State_None; } else { @@ -478,8 +493,8 @@ void CategorizedView::paintEvent(QPaintEvent *event) if (!m_lastDragPosition.isNull()) { - QPair pair = rowDropPos(m_lastDragPosition); - CategorizedViewCategory *category = pair.first; + QPair pair = rowDropPos(m_lastDragPosition); + Group *category = pair.first; int row = pair.second; if (category) { @@ -502,19 +517,21 @@ void CategorizedView::paintEvent(QPaintEvent *event) } } } -void CategorizedView::resizeEvent(QResizeEvent *event) + +void GroupView::resizeEvent(QResizeEvent *event) { QListView::resizeEvent(event); -// if (m_categoryEditor) -// { -// m_categoryEditor->resize(qMax(contentWidth() / 2, m_editedCategory->textRect.width()), m_categoryEditor->height()); -// } + // if (m_categoryEditor) + // { + // m_categoryEditor->resize(qMax(contentWidth() / 2, m_editedCategory->textRect.width()), + //m_categoryEditor->height()); + // } updateGeometries(); } -void CategorizedView::dragEnterEvent(QDragEnterEvent *event) +void GroupView::dragEnterEvent(QDragEnterEvent *event) { if (!isDragEventAccepted(event)) { @@ -524,7 +541,8 @@ void CategorizedView::dragEnterEvent(QDragEnterEvent *event) viewport()->update(); event->accept(); } -void CategorizedView::dragMoveEvent(QDragMoveEvent *event) + +void GroupView::dragMoveEvent(QDragMoveEvent *event) { if (!isDragEventAccepted(event)) { @@ -534,12 +552,14 @@ void CategorizedView::dragMoveEvent(QDragMoveEvent *event) viewport()->update(); event->accept(); } -void CategorizedView::dragLeaveEvent(QDragLeaveEvent *event) + +void GroupView::dragLeaveEvent(QDragLeaveEvent *event) { m_lastDragPosition = QPoint(); viewport()->update(); } -void CategorizedView::dropEvent(QDropEvent *event) + +void GroupView::dropEvent(QDropEvent *event) { m_lastDragPosition = QPoint(); @@ -551,8 +571,8 @@ void CategorizedView::dropEvent(QDropEvent *event) return; } - QPair dropPos = rowDropPos(event->pos() + offset()); - const CategorizedViewCategory *category = dropPos.first; + QPair dropPos = rowDropPos(event->pos() + offset()); + const Group *category = dropPos.first; const int row = dropPos.second; if (row == -1) @@ -564,7 +584,8 @@ void CategorizedView::dropEvent(QDropEvent *event) const QString categoryText = category->text; if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) { - model()->setData(model()->index(row, 0), categoryText, CategorizedViewRoles::CategoryRole); + model()->setData(model()->index(row, 0), categoryText, + CategorizedViewRoles::CategoryRole); event->setDropAction(Qt::MoveAction); event->accept(); } @@ -572,7 +593,7 @@ void CategorizedView::dropEvent(QDropEvent *event) viewport()->update(); } -void CategorizedView::startDrag(Qt::DropActions supportedActions) +void GroupView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = selectionModel()->selectedIndexes(); if (indexes.count() > 0) @@ -585,13 +606,14 @@ void CategorizedView::startDrag(Qt::DropActions supportedActions) QRect rect; QPixmap pixmap = renderToPixmap(indexes, &rect); rect.translate(offset()); - //rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); + // rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); drag->setHotSpot(m_pressedPosition - rect.topLeft()); Qt::DropAction defaultDropAction = Qt::IgnoreAction; - if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) + if (this->defaultDropAction() != Qt::IgnoreAction && + (supportedActions & this->defaultDropAction())) { defaultDropAction = this->defaultDropAction(); } @@ -599,7 +621,8 @@ void CategorizedView::startDrag(Qt::DropActions supportedActions) { const QItemSelection selection = selectionModel()->selection(); - for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) { + for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) + { QModelIndex parent = (*it).parent(); if ((*it).left() != 0) { @@ -616,21 +639,21 @@ void CategorizedView::startDrag(Qt::DropActions supportedActions) } } -QRect CategorizedView::visualRect(const QModelIndex &index) const +QRect GroupView::visualRect(const QModelIndex &index) const { if (!index.isValid() || isIndexHidden(index) || index.column() > 0) { return QRect(); } - const CategorizedViewCategory *cat = category(index); + const Group *cat = category(index); QPair pos = categoryInternalPosition(index); int x = pos.first; int y = pos.second; QRect out; out.setTop(cat->top() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); - out.setLeft(spacing() + x * itemWidth() + x * spacing()); + out.setLeft(/*spacing*/ 10 + x * itemWidth() + x * /*spacing()*/ 10); out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); return out; @@ -649,7 +672,8 @@ void CategorizedView::startCategoryEditor(Category *category) m_categoryEditor->setGeometry(rect); m_categoryEditor->show(); m_categoryEditor->setFocus(); - connect(m_categoryEditor, &QLineEdit::returnPressed, this, &CategorizedView::endCategoryEditor); + connect(m_categoryEditor, &QLineEdit::returnPressed, this, +&CategorizedView::endCategoryEditor); } void CategorizedView::endCategoryEditor() @@ -662,7 +686,8 @@ void CategorizedView::endCategoryEditor() m_updatesDisabled = true; foreach (const QModelIndex &index, itemsForCategory(m_editedCategory)) { - const_cast(index.model())->setData(index, m_categoryEditor->text(), CategoryRole); + const_cast(index.model())->setData(index, +m_categoryEditor->text(), CategoryRole); } m_updatesDisabled = false; delete m_categoryEditor; @@ -672,7 +697,7 @@ void CategorizedView::endCategoryEditor() } */ -QModelIndex CategorizedView::indexAt(const QPoint &point) const +QModelIndex GroupView::indexAt(const QPoint &point) const { for (int i = 0; i < model()->rowCount(); ++i) { @@ -684,7 +709,9 @@ QModelIndex CategorizedView::indexAt(const QPoint &point) const } return QModelIndex(); } -void CategorizedView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) + +void GroupView::setSelection(const QRect &rect, + const QItemSelectionModel::SelectionFlags commands) { for (int i = 0; i < model()->rowCount(); ++i) { @@ -697,10 +724,10 @@ void CategorizedView::setSelection(const QRect &rect, const QItemSelectionModel: update(); } -QPixmap CategorizedView::renderToPixmap(const QModelIndexList &indices, QRect *r) const +QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const { Q_ASSERT(r); - QList > paintPairs = draggablePaintPairs(indices, r); + auto paintPairs = draggablePaintPairs(indices, r); if (paintPairs.isEmpty()) { return QPixmap(); @@ -718,16 +745,20 @@ QPixmap CategorizedView::renderToPixmap(const QModelIndexList &indices, QRect *r } return pixmap; } -QList > CategorizedView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const + +QList> +GroupView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const { Q_ASSERT(r); QRect &rect = *r; const QRect viewportRect = viewport()->rect(); - QList > ret; - for (int i = 0; i < indices.count(); ++i) { + QList> ret; + for (int i = 0; i < indices.count(); ++i) + { const QModelIndex &index = indices.at(i); const QRect current = visualRect(index); - if (current.intersects(viewportRect)) { + if (current.intersects(viewportRect)) + { ret += qMakePair(current, index); rect |= current; } @@ -736,7 +767,7 @@ QList > CategorizedView::draggablePaintPairs(const QMo return ret; } -bool CategorizedView::isDragEventAccepted(QDropEvent *event) +bool GroupView::isDragEventAccepted(QDropEvent *event) { if (event->source() != this) { @@ -746,19 +777,21 @@ bool CategorizedView::isDragEventAccepted(QDropEvent *event) { return false; } - if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), rowDropPos(event->pos()).second, 0, QModelIndex())) + if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), + rowDropPos(event->pos()).second, 0, QModelIndex())) { return false; } return true; } -QPair CategorizedView::rowDropPos(const QPoint &pos) + +QPair GroupView::rowDropPos(const QPoint &pos) { // check that we aren't on a category header and calculate which category we're in - CategorizedViewCategory *category = 0; + Group *category = 0; { int y = 0; - foreach (CategorizedViewCategory *cat, m_categories) + foreach(Group * cat, m_categories) { if (pos.y() > y && pos.y() < (y + cat->headerHeight())) { @@ -789,12 +822,9 @@ QPair CategorizedView::rowDropPos(const QPoint & } else { - for (int i = 0, c = 0; - i < contentWidth(); - i += itemWidth + spacing(), ++c) + for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c) { - if (pos.x() > (i - itemWidth / 2) && - pos.x() <= (i + itemWidth / 2)) + if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2)) { internalColumn = c; break; @@ -812,7 +842,8 @@ QPair CategorizedView::rowDropPos(const QPoint & { // FIXME rework the drag and drop code const int top = category->top(); - for (int r = 0, h = top; r < category->numRows(); h += itemHeightForCategoryRow(category, r), ++r) + for (int r = 0, h = top; r < category->numRows(); + h += itemHeightForCategoryRow(category, r), ++r) { if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r))) { @@ -844,7 +875,7 @@ QPair CategorizedView::rowDropPos(const QPoint & return qMakePair(category, indices.at(categoryRow).row()); } -QPoint CategorizedView::offset() const +QPoint GroupView::offset() const { return QPoint(horizontalOffset(), verticalOffset()); } diff --git a/CategorizedView.h b/GroupView.h similarity index 68% rename from CategorizedView.h rename to GroupView.h index 0550c7f8..e949d892 100644 --- a/CategorizedView.h +++ b/GroupView.h @@ -1,5 +1,4 @@ -#ifndef WIDGET_H -#define WIDGET_H +#pragma once #include #include @@ -14,22 +13,25 @@ struct CategorizedViewRoles }; }; -struct CategorizedViewCategory; +struct Group; -class CategorizedView : public QListView +class GroupView : public QListView { Q_OBJECT public: - CategorizedView(QWidget *parent = 0); - ~CategorizedView(); + GroupView(QWidget *parent = 0); + ~GroupView(); virtual QRect visualRect(const QModelIndex &index) const; QModelIndex indexAt(const QPoint &point) const; - void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; + void setSelection(const QRect &rect, + const QItemSelectionModel::SelectionFlags commands) override; -protected slots: - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); +protected +slots: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QVector &roles); virtual void rowsInserted(const QModelIndex &parent, int start, int end); virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); virtual void updateGeometries(); @@ -51,20 +53,20 @@ protected: void startDrag(Qt::DropActions supportedActions) override; private: - friend struct CategorizedViewCategory; + friend struct Group; - QList m_categories; + QList m_categories; int m_leftMargin; int m_rightMargin; int m_bottomMargin; int m_categoryMargin; - //bool m_updatesDisabled; + // bool m_updatesDisabled; - CategorizedViewCategory *category(const QModelIndex &index) const; - CategorizedViewCategory *category(const QString &cat) const; - CategorizedViewCategory *categoryAt(const QPoint &pos) const; + Group *category(const QModelIndex &index) const; + Group *category(const QString &cat) const; + Group *categoryAt(const QPoint &pos) const; int itemsPerRow() const; int contentWidth() const; @@ -84,22 +86,22 @@ private: QPoint m_pressedPosition; QPersistentModelIndex m_pressedIndex; bool m_pressedAlreadySelected; - CategorizedViewCategory *m_pressedCategory; + Group *m_pressedCategory; QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; QPoint m_lastDragPosition; QPair categoryInternalPosition(const QModelIndex &index) const; int categoryInternalRowTop(const QModelIndex &index) const; - int itemHeightForCategoryRow(const CategorizedViewCategory *category, const int internalRow) const; + int itemHeightForCategoryRow(const Group *category, + const int internalRow) const; QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; - QList > draggablePaintPairs(const QModelIndexList &indices, QRect *r) const; + QList> draggablePaintPairs(const QModelIndexList &indices, + QRect *r) const; bool isDragEventAccepted(QDropEvent *event); - QPair rowDropPos(const QPoint &pos); + QPair rowDropPos(const QPoint &pos); QPoint offset() const; }; - -#endif // WIDGET_H diff --git a/CategorizedProxyModel.cpp b/GroupedProxyModel.cpp similarity index 55% rename from CategorizedProxyModel.cpp rename to GroupedProxyModel.cpp index efcf13c8..ab00a412 100644 --- a/CategorizedProxyModel.cpp +++ b/GroupedProxyModel.cpp @@ -1,12 +1,12 @@ -#include "CategorizedProxyModel.h" +#include "GroupedProxyModel.h" -#include "CategorizedView.h" +#include "GroupView.h" -CategorizedProxyModel::CategorizedProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) +GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } -bool CategorizedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const + +bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { const QString leftCategory = left.data(CategorizedViewRoles::CategoryRole).toString(); const QString rightCategory = right.data(CategorizedViewRoles::CategoryRole).toString(); diff --git a/GroupedProxyModel.h b/GroupedProxyModel.h new file mode 100644 index 00000000..cae87ecd --- /dev/null +++ b/GroupedProxyModel.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class GroupedProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + GroupedProxyModel(QObject *parent = 0); + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; +}; diff --git a/InstanceDelegate.cpp b/InstanceDelegate.cpp index 50cead55..056db99d 100644 --- a/InstanceDelegate.cpp +++ b/InstanceDelegate.cpp @@ -20,7 +20,7 @@ #include #include -#include "CategorizedView.h" +#include "GroupView.h" // Origin: Qt static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, @@ -88,7 +88,8 @@ void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, cons } // TODO this can be made a lot prettier -void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option, const int value, const int maximum) +void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option, + const int value, const int maximum) { if (maximum == 0 || value == maximum) { @@ -251,7 +252,8 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti line.draw(painter, position); } - drawProgressOverlay(painter, opt, index.data(CategorizedViewRoles::ProgressValueRole).toInt(), + drawProgressOverlay(painter, opt, + index.data(CategorizedViewRoles::ProgressValueRole).toInt(), index.data(CategorizedViewRoles::ProgressMaximumRole).toInt()); painter->restore(); diff --git a/InstanceDelegate.h b/InstanceDelegate.h index 6f924405..de2f429b 100644 --- a/InstanceDelegate.h +++ b/InstanceDelegate.h @@ -20,8 +20,10 @@ class ListViewDelegate : public QStyledItemDelegate { public: - explicit ListViewDelegate ( QObject* parent = 0 ); + explicit ListViewDelegate(QObject *parent = 0); + protected: - void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; - QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; }; diff --git a/main.cpp b/main.cpp index bd6a44f9..b9ecde8f 100644 --- a/main.cpp +++ b/main.cpp @@ -5,8 +5,8 @@ #include #include -#include "CategorizedView.h" -#include "CategorizedProxyModel.h" +#include "GroupView.h" +#include "GroupedProxyModel.h" #include "InstanceDelegate.h" Progresser *progresser; @@ -25,18 +25,20 @@ QPixmap icon(const int number) font.setBold(true); font.setPixelSize(28); painter.setFont(font); - painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter, QString::number(number)); + painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter, + QString::number(number)); painter.end(); return p; } -QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, const QString &category) +QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, + const QString &category) { QStandardItem *item = new QStandardItem; item->setText(text); item->setData(icon(color), Qt::DecorationRole); item->setData(category, CategorizedViewRoles::CategoryRole); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - //progresser->addTrackedIndex(item); + // progresser->addTrackedIndex(item); return item; } QStandardItem *createItem(const int index, const QString &category) @@ -46,7 +48,7 @@ QStandardItem *createItem(const int index, const QString &category) item->setData(icon(index), Qt::DecorationRole); item->setData(category, CategorizedViewRoles::CategoryRole); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - //progresser->addTrackedIndex(item); + // progresser->addTrackedIndex(item); return item; } @@ -62,7 +64,10 @@ int main(int argc, char *argv[]) model.setRowCount(10); model.setColumnCount(1); - model.setItem(0, createItem(Qt::red, "Red is a color. Some more text. I'm out of ideas. 42. What's your name?", "Colorful")); + model.setItem( + 0, createItem(Qt::red, + "Red is a color. Some more text. I'm out of ideas. 42. What's your name?", + "Colorful")); model.setItem(1, createItem(Qt::blue, "Blue", "Colorful")); model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful")); @@ -77,13 +82,13 @@ int main(int argc, char *argv[]) for (int i = 0; i < 20; ++i) { - model.setItem(i + 10, createItem(i+1, "Items 1-20")); + model.setItem(i + 10, createItem(i + 1, "Items 1-20")); } - CategorizedProxyModel pModel; + GroupedProxyModel pModel; pModel.setSourceModel(&model); - CategorizedView w; + GroupView w; w.setItemDelegate(new ListViewDelegate); w.setModel(&pModel); w.resize(640, 480); diff --git a/main.h b/main.h index f4c7a3f8..a1e7f432 100644 --- a/main.h +++ b/main.h @@ -1,5 +1,4 @@ -#ifndef MAIN_H -#define MAIN_H +#pragma once #include #include @@ -7,7 +6,7 @@ #include #include -#include "CategorizedView.h" +#include "GroupView.h" class Progresser : public QObject { @@ -27,10 +26,11 @@ public: return item; } -public slots: +public +slots: void timeout() { - foreach (QStandardItem *item, m_items) + foreach(QStandardItem * item, m_items) { int value = item->data(CategorizedViewRoles::ProgressValueRole).toInt(); value += qrand() % 3; @@ -49,5 +49,3 @@ public slots: private: QList m_items; }; - -#endif // MAIN_H From 179451d5911ccce569492717b1f0b0186e25cab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 31 Jan 2014 22:58:57 +0100 Subject: [PATCH 25/49] More reformat. --- Group.cpp | 3 +-- GroupView.cpp | 25 +++++++++++++------------ GroupView.h | 3 +-- main.h | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Group.cpp b/Group.cpp index f23066c5..a62f592b 100644 --- a/Group.cpp +++ b/Group.cpp @@ -6,8 +6,7 @@ #include "GroupView.h" -Group::Group(const QString &text, GroupView *view) - : view(view), text(text), collapsed(false) +Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) { } Group::Group(const Group *other) diff --git a/GroupView.cpp b/GroupView.cpp index e3bc1055..94682df8 100644 --- a/GroupView.cpp +++ b/GroupView.cpp @@ -30,11 +30,11 @@ GroupView::GroupView(QWidget *parent) m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) { setViewMode(IconMode); - //setMovement(Snap); + // setMovement(Snap); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setWordWrap(true); - //setDragDropMode(QListView::InternalMove); + // setDragDropMode(QListView::InternalMove); setAcceptDrops(true); setSpacing(10); } @@ -46,7 +46,7 @@ GroupView::~GroupView() } void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVector &roles) + const QVector &roles) { // if (m_updatesDisabled) // { @@ -262,8 +262,7 @@ int GroupView::categoryInternalRowTop(const QModelIndex &index) const return result; } -int GroupView::itemHeightForCategoryRow(const Group *category, - const int internalRow) const +int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const { for (auto &i : category->items()) { @@ -473,7 +472,8 @@ void GroupView::paintEvent(QPaintEvent *event) QStyleOptionViewItemV4 option(viewOptions()); option.rect = visualRect(index); option.widget = this; - option.features |= QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway? + option.features |= + QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway? if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) { option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected @@ -524,8 +524,9 @@ void GroupView::resizeEvent(QResizeEvent *event) // if (m_categoryEditor) // { - // m_categoryEditor->resize(qMax(contentWidth() / 2, m_editedCategory->textRect.width()), - //m_categoryEditor->height()); + // m_categoryEditor->resize(qMax(contentWidth() / 2, + //m_editedCategory->textRect.width()), + // m_categoryEditor->height()); // } updateGeometries(); @@ -711,7 +712,7 @@ QModelIndex GroupView::indexAt(const QPoint &point) const } void GroupView::setSelection(const QRect &rect, - const QItemSelectionModel::SelectionFlags commands) + const QItemSelectionModel::SelectionFlags commands) { for (int i = 0; i < model()->rowCount(); ++i) { @@ -746,8 +747,8 @@ QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) cons return pixmap; } -QList> -GroupView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const +QList> GroupView::draggablePaintPairs(const QModelIndexList &indices, + QRect *r) const { Q_ASSERT(r); QRect &rect = *r; @@ -791,7 +792,7 @@ QPair GroupView::rowDropPos(const QPoint &pos) Group *category = 0; { int y = 0; - foreach(Group * cat, m_categories) + for (auto cat : m_categories) { if (pos.y() > y && pos.y() < (y + cat->headerHeight())) { diff --git a/GroupView.h b/GroupView.h index e949d892..6d55a462 100644 --- a/GroupView.h +++ b/GroupView.h @@ -92,8 +92,7 @@ private: QPair categoryInternalPosition(const QModelIndex &index) const; int categoryInternalRowTop(const QModelIndex &index) const; - int itemHeightForCategoryRow(const Group *category, - const int internalRow) const; + int itemHeightForCategoryRow(const Group *category, const int internalRow) const; QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; QList> draggablePaintPairs(const QModelIndexList &indices, diff --git a/main.h b/main.h index a1e7f432..47377f7a 100644 --- a/main.h +++ b/main.h @@ -30,7 +30,7 @@ public slots: void timeout() { - foreach(QStandardItem * item, m_items) + for (auto item : m_items) { int value = item->data(CategorizedViewRoles::ProgressValueRole).toInt(); value += qrand() % 3; From 4a9e213238234b03fdd994143726a3f75290fc26 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 1 Feb 2014 14:52:21 +0100 Subject: [PATCH 26/49] Change the OneSix library view. It now shows a list of patches. --- gui/dialogs/OneSixModEditDialog.cpp | 63 +++++++++++++++++++++++++---- gui/dialogs/OneSixModEditDialog.h | 5 ++- gui/dialogs/OneSixModEditDialog.ui | 36 ++++------------- logic/ForgeInstaller.cpp | 6 +++ logic/ForgeInstaller.h | 1 + logic/LiteLoaderInstaller.cpp | 4 ++ logic/OneSixInstance.cpp | 5 ++- logic/OneSixVersion.cpp | 55 ++++++++++++++++++++++--- logic/OneSixVersion.h | 17 ++++++++ logic/OneSixVersionBuilder.cpp | 40 +++++++++++++++--- 10 files changed, 183 insertions(+), 49 deletions(-) diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index ad15311b..a34409f5 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -55,6 +55,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) main_model->setSourceModel(m_version.get()); ui->libraryTreeView->setModel(main_model); ui->libraryTreeView->installEventFilter(this); + connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &OneSixModEditDialog::versionCurrent); updateVersionControls(); } else @@ -93,10 +95,8 @@ OneSixModEditDialog::~OneSixModEditDialog() void OneSixModEditDialog::updateVersionControls() { - bool customVersion = m_inst->versionIsCustom(); ui->forgeBtn->setEnabled(true); ui->liteloaderBtn->setEnabled(LiteLoaderInstaller().canApply(m_inst)); - ui->customEditorBtn->setEnabled(customVersion); ui->mainClassEdit->setText(m_version->mainClass); } @@ -104,23 +104,52 @@ void OneSixModEditDialog::disableVersionControls() { ui->forgeBtn->setEnabled(false); ui->liteloaderBtn->setEnabled(false); - ui->customEditorBtn->setEnabled(false); + ui->reloadLibrariesBtn->setEnabled(false); + ui->removeLibraryBtn->setEnabled(false); ui->mainClassEdit->setText(""); } -void OneSixModEditDialog::on_customEditorBtn_clicked() +void OneSixModEditDialog::on_userEditorBtn_clicked() { - if (QDir(m_inst->instanceRoot()).exists("custom.json")) + if (QDir(m_inst->instanceRoot()).exists("user.json")) { - if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json")) + if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/user.json")) { - QMessageBox::warning(this, tr("Error"), tr("Unable to open custom.json, check the settings")); + QMessageBox::warning(this, tr("Error"), tr("Unable to open user.json, check the settings")); + } + } +} + +void OneSixModEditDialog::on_reloadLibrariesBtn_clicked() +{ + m_inst->reloadVersion(this); +} + +void OneSixModEditDialog::on_removeLibraryBtn_clicked() +{ + if (ui->libraryTreeView->currentIndex().isValid()) + { + if (!m_version->remove(ui->libraryTreeView->currentIndex().row())) + { + QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file")); + } + else + { + m_inst->reloadVersion(this); } } } void OneSixModEditDialog::on_forgeBtn_clicked() { + if (QDir(m_inst->instanceRoot()).exists("custom.json")) + { + if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes) + { + return; + } + QDir(m_inst->instanceRoot()).remove("custom.json"); + } VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); vselect.setFilter(1, m_inst->currentVersionId()); if (vselect.exec() && vselect.selectedVersion()) @@ -167,6 +196,14 @@ void OneSixModEditDialog::on_forgeBtn_clicked() void OneSixModEditDialog::on_liteloaderBtn_clicked() { + if (QDir(m_inst->instanceRoot()).exists("custom.json")) + { + if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes) + { + return; + } + QDir(m_inst->instanceRoot()).remove("custom.json"); + } LiteLoaderInstaller liteloader; if (!liteloader.canApply(m_inst)) { @@ -304,3 +341,15 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou Mod &m = m_mods->operator[](row); ui->frame->updateWithMod(m); } + +void OneSixModEditDialog::versionCurrent(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (!current.isValid()) + { + ui->removeLibraryBtn->setDisabled(true); + } + else + { + ui->removeLibraryBtn->setEnabled(m_version->canRemove(current.row())); + } +} diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h index 161604c9..7e759f48 100644 --- a/gui/dialogs/OneSixModEditDialog.h +++ b/gui/dialogs/OneSixModEditDialog.h @@ -45,7 +45,9 @@ slots: void on_buttonBox_rejected(); void on_forgeBtn_clicked(); void on_liteloaderBtn_clicked(); - void on_customEditorBtn_clicked(); + void on_userEditorBtn_clicked(); + void on_reloadLibrariesBtn_clicked(); + void on_removeLibraryBtn_clicked(); void updateVersionControls(); void disableVersionControls(); @@ -64,4 +66,5 @@ private: public slots: void loaderCurrent(QModelIndex current, QModelIndex previous); + void versionCurrent(const QModelIndex ¤t, const QModelIndex &previous); }; diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui index 67642c23..d90c6d93 100644 --- a/gui/dialogs/OneSixModEditDialog.ui +++ b/gui/dialogs/OneSixModEditDialog.ui @@ -26,7 +26,7 @@ - 1 + 0 @@ -35,13 +35,6 @@ - - - - Tweakers: - - - @@ -51,7 +44,7 @@ Qt::ScrollBarAlwaysOff - false + true @@ -96,37 +89,22 @@ - - QFrame::Sunken - Qt::Horizontal - - - false - - - Add new libraries - + - &Add + Reload - - false - - - Remove selected libraries - - &Remove + Remove @@ -138,9 +116,9 @@ - + - Open custom.json + Open user.json diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 8d2445ed..c7160d6e 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -74,6 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) QJsonObject installObj = installVal.toObject(); QString libraryName = installObj.value("path").toString(); internalPath = installObj.value("filePath").toString(); + m_forgeVersionString = installObj.value("version").toString().remove("Forge").trimmed(); // where do we put the library? decode the mojang path OneSixLibrary lib(libraryName); @@ -204,6 +205,11 @@ bool ForgeInstaller::add(OneSixInstance *to) } } + obj.insert("name", QString("Forge")); + obj.insert("id", id()); + obj.insert("version", m_forgeVersionString); + obj.insert("mcVersion", to->intendedVersionId()); + QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) { diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index eb8ecd79..c5052092 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -37,5 +37,6 @@ private: QString internalPath; QString finalPath; QString realVersionId; + QString m_forgeVersionString; QString m_universal_url; }; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index f658f834..48bbaeff 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -78,6 +78,10 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) } obj.insert("+libraries", libraries); + obj.insert("name", QString("LiteLoader")); + obj.insert("id", id()); + obj.insert("version", to->intendedVersionId()); + obj.insert("mcVersion", to->intendedVersionId()); QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 19d5e112..88c40316 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -319,8 +319,9 @@ bool OneSixInstance::shouldUpdate() const bool OneSixInstance::versionIsCustom() { QDir patches(PathCombine(instanceRoot(), "patches/")); - return QFile::exists(PathCombine(instanceRoot(), "custom.json")) - || (patches.exists() && patches.count() >= 0); + return (patches.exists() && patches.count() >= 0) + || QFile::exists(PathCombine(instanceRoot(), "custom.json")) + || QFile::exists(PathCombine(instanceRoot(), "user.json")); } QString OneSixInstance::currentVersionId() const diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 1abd5114..0b6d1986 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -16,6 +16,7 @@ #include "OneSixVersion.h" #include +#include #include "OneSixVersionBuilder.h" @@ -47,6 +48,7 @@ void OneSixVersion::clear() mainClass.clear(); libraries.clear(); tweakers.clear(); + versionFiles.clear(); endResetModel(); } @@ -70,6 +72,24 @@ void OneSixVersion::dump() const qDebug().nospace() << "\n)"; } +bool OneSixVersion::canRemove(const int index) const +{ + if (index < versionFiles.size()) + { + return versionFiles.at(index).id != "org.multimc.version.json"; + } + return false; +} + +bool OneSixVersion::remove(const int index) +{ + if (canRemove(index)) + { + return QFile::remove(versionFiles.at(index).filename); + } + return false; +} + QList > OneSixVersion::getActiveNormalLibs() { QList > output; @@ -114,14 +134,39 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const int row = index.row(); int column = index.column(); - if (row < 0 || row >= tweakers.size()) + if (row < 0 || row >= versionFiles.size()) return QVariant(); if (role == Qt::DisplayRole) { - if (column == 0) + switch (column) { - return tweakers.at(row); + case 0: + return versionFiles.at(row).name; + case 1: + return versionFiles.at(row).version; + default: + return QVariant(); + } + } + return QVariant(); +} + +QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) + { + if (role == Qt::DisplayRole) + { + switch (section) + { + case 0: + return tr("Name"); + case 1: + return tr("Version"); + default: + return QVariant(); + } } } return QVariant(); @@ -136,12 +181,12 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const int OneSixVersion::rowCount(const QModelIndex &parent) const { - return tweakers.size(); + return versionFiles.size(); } int OneSixVersion::columnCount(const QModelIndex &parent) const { - return 1; + return 2; } QDebug operator<<(QDebug &dbg, const OneSixVersion *version) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 98a4b418..d7a6684d 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -32,6 +32,7 @@ public: explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; @@ -41,6 +42,12 @@ public: void dump() const; + bool canRemove(const int index) const; + +public +slots: + bool remove(const int index); + public: QList> getActiveNormalLibs(); QList> getActiveNativeLibs(); @@ -109,6 +116,16 @@ public: */ // QList rules; + struct VersionFile + { + QString name; + QString id; + QString version; + QString mcVersion; + QString filename; + }; + QList versionFiles; + private: OneSixInstance *m_instance; }; diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index a8e7253b..08506d4b 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -34,6 +34,14 @@ struct VersionFile { int order; + QString name; + QString fileId; + QString version; + // TODO use the mcVersion to determine if a version file should be removed on update + QString mcVersion; + QString filename; + // TODO requirements + // QMap requirements; QString id; QString mainClass; QString overwriteMinecraftArguments; @@ -216,6 +224,12 @@ struct VersionFile } } + out.name = root.value("name").toString(); + out.fileId = root.value("id").toString(); + out.version = root.value("version").toString(); + out.mcVersion = root.value("mcVersion").toString(); + out.filename = filename; + auto readString = [root, filename](const QString &key, QString &variable) { if (root.contains(key)) @@ -661,6 +675,14 @@ struct VersionFile } } + OneSixVersion::VersionFile versionFile; + versionFile.name = name; + versionFile.id = fileId; + versionFile.version = this->version; + versionFile.mcVersion = mcVersion; + versionFile.filename = filename; + version->versionFiles.append(versionFile); + isError = false; } }; @@ -727,6 +749,10 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) { return false; } + file.name = "version.json"; + file.fileId = "org.multimc.version.json"; + file.version = m_instance->intendedVersionId(); + file.mcVersion = m_instance->intendedVersionId(); bool isError = false; file.applyTo(m_version, isError); if (isError) @@ -773,17 +799,21 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) } } - // instance.json + // user.json if (!excludeCustom) { - if (QFile::exists(root.absoluteFilePath("instance.json"))) + if (QFile::exists(root.absoluteFilePath("user.json"))) { - QLOG_INFO() << "Reading instance.json"; + QLOG_INFO() << "Reading user.json"; VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("instance.json")), false, &file)) + if (!read(QFileInfo(root.absoluteFilePath("user.json")), false, &file)) { return false; } + file.name = "user.json"; + file.fileId = "org.multimc.user.json"; + file.version = QString(); + file.mcVersion = QString(); bool isError = false; file.applyTo(m_version, isError); if (isError) @@ -792,7 +822,7 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) m_widgetParent, QObject::tr("Error"), QObject::tr( "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("instance.json"))); + .arg(root.absoluteFilePath("user.json"))); return false; } } From 8637cce4333aaf56a231d5fab866b0e770436783 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 1 Feb 2014 16:26:38 +0100 Subject: [PATCH 27/49] Fix a bug --- gui/dialogs/OneSixModEditDialog.cpp | 15 ++++++++++++--- logic/ForgeInstaller.cpp | 2 +- logic/LiteLoaderInstaller.cpp | 2 +- logic/OneSixVersionBuilder.cpp | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index a34409f5..ebd685e8 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -111,12 +111,21 @@ void OneSixModEditDialog::disableVersionControls() void OneSixModEditDialog::on_userEditorBtn_clicked() { - if (QDir(m_inst->instanceRoot()).exists("user.json")) + QDir root(m_inst->instanceRoot()); + if (!root.exists("user.json")) { - if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/user.json")) + QFile file(root.absoluteFilePath("user.json")); + if (!file.open(QFile::WriteOnly)) { - QMessageBox::warning(this, tr("Error"), tr("Unable to open user.json, check the settings")); + QMessageBox::critical(this, tr("Error"), tr("Couldn't write a skeletion user.json file: %1").arg(file.errorString())); + return; } + file.write("{\n}"); + file.close(); + } + if (!MMC->openJsonEditor(root.absoluteFilePath("user.json"))) + { + QMessageBox::warning(this, tr("Error"), tr("Unable to open user.json, check the settings")); } } diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index c7160d6e..47c42694 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -206,7 +206,7 @@ bool ForgeInstaller::add(OneSixInstance *to) } obj.insert("name", QString("Forge")); - obj.insert("id", id()); + obj.insert("fileId", id()); obj.insert("version", m_forgeVersionString); obj.insert("mcVersion", to->intendedVersionId()); diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 48bbaeff..60a43d49 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -79,7 +79,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) obj.insert("+libraries", libraries); obj.insert("name", QString("LiteLoader")); - obj.insert("id", id()); + obj.insert("fileId", id()); obj.insert("version", to->intendedVersionId()); obj.insert("mcVersion", to->intendedVersionId()); diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 08506d4b..a541766b 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -225,7 +225,7 @@ struct VersionFile } out.name = root.value("name").toString(); - out.fileId = root.value("id").toString(); + out.fileId = root.value("fileId").toString(); out.version = root.value("version").toString(); out.mcVersion = root.value("mcVersion").toString(); out.filename = filename; From 866d7029afa10293a57d71aecbe3629399c95d06 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 1 Feb 2014 19:42:47 +0100 Subject: [PATCH 28/49] Fix some bugs that got uncovered while trying to get liteloader 1.7 to work --- logic/OneSixVersionBuilder.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index a541766b..9832d4a0 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -483,8 +483,15 @@ struct VersionFile static std::shared_ptr createLibrary(const Library &lib) { std::shared_ptr out(new OneSixLibrary(lib.name)); - out->setBaseUrl(lib.url); + if (!lib.url.isEmpty()) + { + out->setBaseUrl(lib.url); + } out->setHint(lib.hint); + if (!lib.absoluteUrl.isEmpty()) + { + out->setAbsoluteUrl(lib.absoluteUrl); + } out->setAbsoluteUrl(lib.absoluteUrl); out->extract_excludes = lib.excludes; for (auto native : lib.natives) @@ -725,6 +732,10 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) { return false; } + file.name = "custom.json"; + file.filename = "custom.json"; + file.fileId = "org.multimc.custom.json"; + file.version = QString(); bool isError = false; file.applyTo(m_version, isError); if (isError) From 790402bdce96a4ce67b32d228aa251fc4d184f5e Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 1 Feb 2014 22:32:48 +0100 Subject: [PATCH 29/49] Disable anything related to user.json for now. Will be re-enabled once we have a gui for it. --- gui/dialogs/OneSixModEditDialog.cpp | 20 -------------------- gui/dialogs/OneSixModEditDialog.h | 1 - gui/dialogs/OneSixModEditDialog.ui | 16 +--------------- logic/OneSixVersionBuilder.cpp | 4 +++- 4 files changed, 4 insertions(+), 37 deletions(-) diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index 1742ff80..41ac5d8d 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -109,26 +109,6 @@ void OneSixModEditDialog::disableVersionControls() ui->mainClassEdit->setText(""); } -void OneSixModEditDialog::on_userEditorBtn_clicked() -{ - QDir root(m_inst->instanceRoot()); - if (!root.exists("user.json")) - { - QFile file(root.absoluteFilePath("user.json")); - if (!file.open(QFile::WriteOnly)) - { - QMessageBox::critical(this, tr("Error"), tr("Couldn't write a skeletion user.json file: %1").arg(file.errorString())); - return; - } - file.write("{\n}"); - file.close(); - } - if (!MMC->openJsonEditor(root.absoluteFilePath("user.json"))) - { - QMessageBox::warning(this, tr("Error"), tr("Unable to open user.json, check the settings")); - } -} - void OneSixModEditDialog::on_reloadLibrariesBtn_clicked() { m_inst->reloadVersion(this); diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h index 7e759f48..a7def967 100644 --- a/gui/dialogs/OneSixModEditDialog.h +++ b/gui/dialogs/OneSixModEditDialog.h @@ -45,7 +45,6 @@ slots: void on_buttonBox_rejected(); void on_forgeBtn_clicked(); void on_liteloaderBtn_clicked(); - void on_userEditorBtn_clicked(); void on_reloadLibrariesBtn_clicked(); void on_removeLibraryBtn_clicked(); void updateVersionControls(); diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui index d90c6d93..b97fa323 100644 --- a/gui/dialogs/OneSixModEditDialog.ui +++ b/gui/dialogs/OneSixModEditDialog.ui @@ -44,7 +44,7 @@ Qt::ScrollBarAlwaysOff - true + false @@ -108,20 +108,6 @@ - - - - Qt::Horizontal - - - - - - - Open user.json - - - diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 9832d4a0..4d6fb05a 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -750,7 +750,7 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) } else { - // version.json -> patches/*.json -> instance.json + // version.json -> patches/*.json -> user.json // version.json { @@ -810,6 +810,7 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) } } +#if 0 // user.json if (!excludeCustom) { @@ -838,6 +839,7 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) } } } +#endif } // some final touches From b2bf50a6d75d32ac483bb53d5c5948b353cd2d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 2 Feb 2014 10:26:38 +0100 Subject: [PATCH 30/49] Small tweaks --- GroupView.cpp | 41 +++++++++-------------------------------- GroupView.h | 36 +++++++++++++++++++++++++++++++++++- InstanceDelegate.cpp | 2 +- main.h | 19 +++++++++++-------- 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/GroupView.cpp b/GroupView.cpp index 94682df8..50d19f52 100644 --- a/GroupView.cpp +++ b/GroupView.cpp @@ -26,17 +26,17 @@ template bool listsIntersect(const QList &l1, const QList t2) } GroupView::GroupView(QWidget *parent) - : QListView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), + : QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) { - setViewMode(IconMode); + // setViewMode(IconMode); // setMovement(Snap); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - setWordWrap(true); + // setWordWrap(true); // setDragDropMode(QListView::InternalMove); setAcceptDrops(true); - setSpacing(10); + // setSpacing(10); } GroupView::~GroupView() @@ -48,49 +48,26 @@ GroupView::~GroupView() void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { - // if (m_updatesDisabled) - // { - // return; - // } - - QListView::dataChanged(topLeft, bottomRight, roles); - if (roles.contains(CategorizedViewRoles::CategoryRole) || roles.contains(Qt::DisplayRole)) { updateGeometries(); - update(); } + viewport()->update(); } void GroupView::rowsInserted(const QModelIndex &parent, int start, int end) { - // if (m_updatesDisabled) - // { - // return; - // } - - QListView::rowsInserted(parent, start, end); - updateGeometries(); - update(); + viewport()->update(); } void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { - // if (m_updatesDisabled) - // { - // return; - // } - - QListView::rowsAboutToBeRemoved(parent, start, end); - updateGeometries(); - update(); + viewport()->update(); } void GroupView::updateGeometries() { - QListView::updateGeometries(); - int previousScroll = verticalScrollBar()->value(); QMap cats; @@ -145,7 +122,7 @@ void GroupView::updateGeometries() verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum())); - update(); + viewport()->update(); } bool GroupView::isIndexHidden(const QModelIndex &index) const @@ -520,7 +497,7 @@ void GroupView::paintEvent(QPaintEvent *event) void GroupView::resizeEvent(QResizeEvent *event) { - QListView::resizeEvent(event); + // QListView::resizeEvent(event); // if (m_categoryEditor) // { diff --git a/GroupView.h b/GroupView.h index 6d55a462..bf911794 100644 --- a/GroupView.h +++ b/GroupView.h @@ -15,7 +15,7 @@ struct CategorizedViewRoles struct Group; -class GroupView : public QListView +class GroupView : public QAbstractItemView { Q_OBJECT @@ -28,6 +28,40 @@ public: void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; + /* + * BS + */ + + virtual int horizontalOffset() const override + { + return 0; + } + + virtual int verticalOffset() const override + { + return 0; + } + + virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override + { + return; + } + + virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) + override + { + return QModelIndex(); + } + + virtual QRegion visualRegionForSelection(const QItemSelection &) const override + { + return QRegion(); + } + + /* + * End of BS + */ + protected slots: void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, diff --git a/InstanceDelegate.cpp b/InstanceDelegate.cpp index 056db99d..944cfd76 100644 --- a/InstanceDelegate.cpp +++ b/InstanceDelegate.cpp @@ -103,7 +103,7 @@ void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option color.setAlphaF(0.70f); painter->setBrush(color); painter->setPen(QPen(QBrush(), 0)); - painter->drawPie(option.rect, 90 * 16, -percent * 360 * 60); + painter->drawPie(option.rect, 90 * 16, -percent * 360 * 16); painter->restore(); } diff --git a/main.h b/main.h index 47377f7a..6883e98e 100644 --- a/main.h +++ b/main.h @@ -30,20 +30,23 @@ public slots: void timeout() { + QList toRemove; for (auto item : m_items) { + int maximum = item->data(CategorizedViewRoles::ProgressMaximumRole).toInt(); int value = item->data(CategorizedViewRoles::ProgressValueRole).toInt(); - value += qrand() % 3; - if (value >= item->data(CategorizedViewRoles::ProgressMaximumRole).toInt()) + int newvalue = std::min(value + 3, maximum); + item->setData(newvalue, CategorizedViewRoles::ProgressValueRole); + + if(newvalue >= maximum) { - item->setData(item->data(CategorizedViewRoles::ProgressMaximumRole).toInt(), - CategorizedViewRoles::ProgressValueRole); - } - else - { - item->setData(value, CategorizedViewRoles::ProgressValueRole); + toRemove.append(item); } } + for(auto remove : toRemove) + { + m_items.removeAll(remove); + } } private: From ece826bdbc5ca525e253cafcfef3d93e492949f5 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sun, 2 Feb 2014 14:05:07 +0100 Subject: [PATCH 31/49] Add a MMC-depend field (soft/hard) for version checking --- depends/util/CMakeLists.txt | 3 + depends/util/include/modutils.h | 32 +++++ depends/util/src/modutils.cpp | 216 ++++++++++++++++++++++++++++++++ logic/ForgeInstaller.cpp | 6 +- logic/LiteLoaderInstaller.cpp | 3 +- logic/OneSixLibrary.cpp | 2 +- logic/OneSixLibrary.h | 11 +- logic/OneSixVersionBuilder.cpp | 110 +++++++++++----- 8 files changed, 351 insertions(+), 32 deletions(-) create mode 100644 depends/util/include/modutils.h create mode 100644 depends/util/src/modutils.cpp diff --git a/depends/util/CMakeLists.txt b/depends/util/CMakeLists.txt index db7d70e6..7f6573bd 100644 --- a/depends/util/CMakeLists.txt +++ b/depends/util/CMakeLists.txt @@ -35,6 +35,9 @@ src/userutils.cpp include/cmdutils.h src/cmdutils.cpp + +include/modutils.h +src/modutils.cpp ) # Set the include dir path. diff --git a/depends/util/include/modutils.h b/depends/util/include/modutils.h new file mode 100644 index 00000000..e04db66f --- /dev/null +++ b/depends/util/include/modutils.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "libutil_config.h" + +class QUrl; + +namespace Util +{ +struct Version +{ + Version(const QString &str); + + bool operator<(const Version &other) const; + bool operator<=(const Version &other) const; + bool operator>(const Version &other) const; + bool operator==(const Version &other) const; + bool operator!=(const Version &other) const; + + QString toString() const + { + return m_string; + } + +private: + QString m_string; +}; + +LIBUTIL_EXPORT QUrl expandQMURL(const QString &in); +LIBUTIL_EXPORT bool versionIsInInterval(const QString &version, const QString &interval); +} + diff --git a/depends/util/src/modutils.cpp b/depends/util/src/modutils.cpp new file mode 100644 index 00000000..44a04b72 --- /dev/null +++ b/depends/util/src/modutils.cpp @@ -0,0 +1,216 @@ +#include "include/modutils.h" + +#include +#include +#include +#include + +Util::Version::Version(const QString &str) : m_string(str) +{ +} + +bool Util::Version::operator<(const Version &other) const +{ + QStringList parts1 = m_string.split('.'); + QStringList parts2 = other.m_string.split('.'); + + while (!parts1.isEmpty() && !parts2.isEmpty()) + { + QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst(); + QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst(); + bool ok1 = false; + bool ok2 = false; + int int1 = part1.toInt(&ok1); + int int2 = part2.toInt(&ok2); + if (ok1 && ok2) + { + if (int1 == int2) + { + continue; + } + else + { + return int1 < int2; + } + } + else + { + if (part1 == part2) + { + continue; + } + else + { + return part1 < part2; + } + } + } + + return false; +} +bool Util::Version::operator<=(const Util::Version &other) const +{ + return *this < other || *this == other; +} +bool Util::Version::operator>(const Version &other) const +{ + QStringList parts1 = m_string.split('.'); + QStringList parts2 = other.m_string.split('.'); + + while (!parts1.isEmpty() && !parts2.isEmpty()) + { + QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst(); + QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst(); + bool ok1 = false; + bool ok2 = false; + int int1 = part1.toInt(&ok1); + int int2 = part2.toInt(&ok2); + if (ok1 && ok2) + { + if (int1 == int2) + { + continue; + } + else + { + return int1 > int2; + } + } + else + { + if (part1 == part2) + { + continue; + } + else + { + return part1 > part2; + } + } + } + + return false; +} +bool Util::Version::operator==(const Version &other) const +{ + QStringList parts1 = m_string.split('.'); + QStringList parts2 = other.m_string.split('.'); + + while (!parts1.isEmpty() && !parts2.isEmpty()) + { + QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst(); + QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst(); + bool ok1 = false; + bool ok2 = false; + int int1 = part1.toInt(&ok1); + int int2 = part2.toInt(&ok2); + if (ok1 && ok2) + { + if (int1 == int2) + { + continue; + } + else + { + return false; + } + } + else + { + if (part1 == part2) + { + continue; + } + else + { + return false; + } + } + } + + return true; +} +bool Util::Version::operator!=(const Version &other) const +{ + return !operator==(other); +} + +QUrl Util::expandQMURL(const QString &in) +{ + QUrl inUrl(in); + if (inUrl.scheme() == "github") + { + // needed because QUrl makes the host all lower cases + const QString repo = in.mid(in.indexOf(inUrl.host(), 0, Qt::CaseInsensitive), inUrl.host().size()); + QUrl out; + out.setScheme("https"); + out.setHost("raw.github.com"); + out.setPath(QString("/%1/%2/%3%4") + .arg(inUrl.userInfo(), repo, + inUrl.fragment().isEmpty() ? "master" : inUrl.fragment(), inUrl.path())); + return out; + } + else if (inUrl.scheme() == "mcf") + { + QUrl out; + out.setScheme("http"); + out.setHost("www.minecraftforum.net"); + out.setPath(QString("/topic/%1-").arg(inUrl.path())); + return out; + } + else + { + return in; + } +} + +bool Util::versionIsInInterval(const QString &version, const QString &interval) +{ + if (interval.isEmpty() || version == interval) + { + return true; + } + + // Interval notation is used + QRegularExpression exp( + "(?[\\[\\]\\(\\)])(?.*?)(,(?.*?))?(?[\\[\\]\\(\\)])"); + QRegularExpressionMatch match = exp.match(interval); + if (match.hasMatch()) + { + const QChar start = match.captured("start").at(0); + const QChar end = match.captured("end").at(0); + const QString bottom = match.captured("bottom"); + const QString top = match.captured("top"); + + // check if in range (bottom) + if (!bottom.isEmpty()) + { + if ((start == '[') && !(version >= bottom)) + { + return false; + } + else if ((start == '(') && !(version > bottom)) + { + return false; + } + } + + // check if in range (top) + if (!top.isEmpty()) + { + if ((end == ']') && !(version <= top)) + { + return false; + } + else if ((end == ')') && !(version < top)) + { + return false; + } + } + + return true; + } + + return false; +} + diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 47c42694..6b498d70 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -171,7 +171,11 @@ bool ForgeInstaller::add(OneSixInstance *to) if (!found) { // add lib - libObj.insert("insert", QString("prepend-if-not-exists")); + libObj.insert("insert", QString("prepend")); + if (lib->name() == "minecraftforge") + { + libObj.insert("MMC-depend", QString("hard")); + } sliding_insert_window++; } librariesPlus.prepend(libObj); diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 60a43d49..c363cad6 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -63,7 +63,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]); launchwrapperLib.finalize(); QJsonObject lwLibObj = launchwrapperLib.toJson(); - lwLibObj.insert("insert", QString("prepend-if-not-exists")); + lwLibObj.insert("insert", QString("prepend")); libraries.append(lwLibObj); } @@ -74,6 +74,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) liteloaderLib.finalize(); QJsonObject llLibObj = liteloaderLib.toJson(); llLibObj.insert("insert", QString("prepend")); + llLibObj.insert("MMC-depend", QString("hard")); libraries.append(llLibObj); } diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 2b1f0600..c78679d1 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -46,7 +46,7 @@ void OneSixLibrary::finalize() } m_decentname = parts[1]; - m_decentversion = parts[2]; + m_decentversion = minVersion = parts[2]; m_storage_path = relative; m_download_url = m_base_url + relative; diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 5384015a..371ca6f4 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -60,12 +60,21 @@ private: public: QStringList extract_excludes; + QString minVersion; + + enum DependType + { + Soft, + Hard + }; + DependType dependType; public: /// Constructor - OneSixLibrary(const QString &name) + OneSixLibrary(const QString &name, const DependType type = Soft) { m_name = name; + dependType = type; } /// Returns the raw name field diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 4d6fb05a..55c7d48e 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -29,6 +29,7 @@ #include "OneSixVersion.h" #include "OneSixInstance.h" #include "OneSixRule.h" +#include "modutils.h" #include "logger/QsLog.h" struct VersionFile @@ -78,12 +79,16 @@ struct VersionFile Apply, Append, Prepend, - AppendIfNotExists, - PrependIfNotExists, Replace }; - InsertType insertType; + InsertType insertType = Append; QString insertData; + enum DependType + { + Soft, + Hard + }; + DependType dependType = Soft; }; bool shouldOverwriteLibs = false; QList overwriteLibs; @@ -414,21 +419,13 @@ struct VersionFile { lib.insertType = Library::Apply; } - else if (insertString == "append") - { - lib.insertType = Library::Append; - } else if (insertString == "prepend") { lib.insertType = Library::Prepend; } - else if (insertString == "prepend-if-not-exists") + else if (insertString == "append") { - lib.insertType = Library::PrependIfNotExists; - } - else if (insertString == "append-if-not-exists") - { - lib.insertType = Library::PrependIfNotExists; + lib.insertType = Library::Prepend; } else if (insertString == "replace") { @@ -440,6 +437,24 @@ struct VersionFile << "contains an invalid insert type"; return out; } + if (libObj.contains("MMC-depend") && libObj.value("MMC-depend").isString()) + { + const QString dependString = libObj.value("MMC-depend").toString(); + if (dependString == "hard") + { + lib.dependType = Library::Hard; + } + else if (dependString == "soft") + { + lib.dependType = Library::Soft; + } + else + { + QLOG_ERROR() << "A '+' library in" << filename + << "contains an invalid depend type"; + return out; + } + } out.addLibs.append(lib); } } @@ -629,28 +644,67 @@ struct VersionFile break; } case Library::Append: - version->libraries.append(createLibrary(lib)); - break; case Library::Prepend: - version->libraries.prepend(createLibrary(lib)); - break; - case Library::AppendIfNotExists: { - int index = findLibrary(version->libraries, lib.name); + const int startOfVersion = lib.name.lastIndexOf(':') + 1; + const int index = findLibrary(version->libraries, QString(lib.name).replace(startOfVersion, INT_MAX, '*')); if (index < 0) { - version->libraries.append(createLibrary(lib)); + if (lib.insertType == Library::Append) + { + version->libraries.append(createLibrary(lib)); + } + else + { + version->libraries.prepend(createLibrary(lib)); + } } - break; - } - case Library::PrependIfNotExists: - { - - int index = findLibrary(version->libraries, lib.name); - if (index < 0) + else { - version->libraries.prepend(createLibrary(lib)); + auto otherLib = version->libraries.at(index); + const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX); + const Util::Version otherVersion = otherLib->version(); + // if the existing version is a hard dependency we can either use it or fail, but we can't change it + if (otherLib->dependType == OneSixLibrary::Hard) + { + // we need a higher version, or we're hard to and the versions aren't equal + if (ourVersion > otherVersion || (lib.dependType == Library::Hard && ourVersion != otherVersion)) + { + QLOG_ERROR() << "Error resolving library dependencies between" + << otherLib->rawName() << "and" << lib.name << "in" + << filename; + return; + } + else + { + // the library is already existing, so we don't have to do anything + } + } + else if (otherLib->dependType == OneSixLibrary::Soft) + { + // if we are higher it means we should update + if (ourVersion > otherVersion) + { + auto library = createLibrary(lib); + if (Util::Version(otherLib->minVersion) < ourVersion) + { + library->minVersion = ourVersion.toString(); + } + version->libraries.replace(index, library); + } + else + { + // our version is smaller than the existing version, but we require it: fail + if (lib.dependType == Library::Hard) + { + QLOG_ERROR() << "Error resolving library dependencies between" + << otherLib->rawName() << "and" << lib.name << "in" + << filename; + return; + } + } + } } break; } From cdd35910c3aeb4048e2e982db0b8227f76574704 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sun, 2 Feb 2014 14:17:44 +0100 Subject: [PATCH 32/49] Fix installing forge after liteloader and then removing liteloader. Also formatting. --- logic/ForgeInstaller.cpp | 6 +- logic/OneSixInstance.cpp | 10 +-- logic/OneSixInstance.h | 4 +- logic/OneSixInstance_p.h | 2 +- logic/OneSixVersion.cpp | 4 +- logic/OneSixVersion.h | 2 +- logic/OneSixVersionBuilder.cpp | 146 ++++++++++++++++++--------------- logic/OneSixVersionBuilder.h | 4 +- 8 files changed, 96 insertions(+), 82 deletions(-) diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 6b498d70..3e18d17f 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -151,7 +151,7 @@ bool ForgeInstaller::add(OneSixInstance *to) bool found = false; bool equals = false; // find an entry that matches this one - for (auto tolib : to->getNonCustomVersion()->libraries) + for (auto tolib : to->getVanillaVersion()->libraries) { if (tolib->name() != libName) continue; @@ -194,7 +194,7 @@ bool ForgeInstaller::add(OneSixInstance *to) match = expression.match(args); } } - if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments) + if (!args.isEmpty() && args != to->getVanillaVersion()->minecraftArguments) { obj.insert("minecraftArguments", args); } @@ -203,7 +203,7 @@ bool ForgeInstaller::add(OneSixInstance *to) obj.insert("+tweakers", QJsonArray::fromStringList(tweakers)); } if (!m_forge_version->processArguments.isEmpty() && - m_forge_version->processArguments != to->getNonCustomVersion()->processArguments) + m_forge_version->processArguments != to->getVanillaVersion()->processArguments) { obj.insert("processArguments", m_forge_version->processArguments); } diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 8ecbe7a0..ae172f21 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -35,7 +35,7 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); d->version.reset(new OneSixVersion(this, this)); - d->nonCustomVersion.reset(new OneSixVersion(this, this)); + d->vanillaVersion.reset(new OneSixVersion(this, this)); if (QDir(instanceRoot()).exists("version.json")) { reloadVersion(); @@ -321,7 +321,7 @@ bool OneSixInstance::reloadVersion(QWidget *widgetParent) bool ret = d->version->reload(widgetParent); if (ret) { - ret = d->nonCustomVersion->reload(widgetParent, true); + ret = d->vanillaVersion->reload(widgetParent, true); } emit versionReloaded(); return ret; @@ -331,7 +331,7 @@ void OneSixInstance::clearVersion() { I_D(OneSixInstance); d->version->clear(); - d->nonCustomVersion->clear(); + d->vanillaVersion->clear(); emit versionReloaded(); } @@ -341,10 +341,10 @@ std::shared_ptr OneSixInstance::getFullVersion() const return d->version; } -std::shared_ptr OneSixInstance::getNonCustomVersion() const +std::shared_ptr OneSixInstance::getVanillaVersion() const { I_D(const OneSixInstance); - return d->nonCustomVersion; + return d->vanillaVersion; } QString OneSixInstance::defaultBaseJar() const diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 5aa12e44..ae95eab1 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -57,8 +57,8 @@ public: void clearVersion(); /// get the current full version info std::shared_ptr getFullVersion() const; - /// gets the current version info, excluding custom.json - std::shared_ptr getNonCustomVersion() const; + /// gets the current version info, but only for version.json + std::shared_ptr getVanillaVersion() const; /// is the current version original, or custom? virtual bool versionIsCustom() override; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index 6dc74e50..0cc46f33 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -22,7 +22,7 @@ struct OneSixInstancePrivate : public BaseInstancePrivate { std::shared_ptr version; - std::shared_ptr nonCustomVersion; + std::shared_ptr vanillaVersion; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; }; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 0b6d1986..e19683d8 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -26,10 +26,10 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) clear(); } -bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom) +bool OneSixVersion::reload(QWidget *widgetParent, const bool onlyVanilla) { beginResetModel(); - bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); + bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla); endResetModel(); return ret; } diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index d7a6684d..516f153c 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -37,7 +37,7 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - bool reload(QWidget *widgetParent, const bool excludeCustom = false); + bool reload(QWidget *widgetParent, const bool onlyVanilla = false); void clear(); void dump() const; diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 55c7d48e..03555070 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -280,7 +280,8 @@ struct VersionFile QJsonValue tweakersVal = root.value("tweakers"); if (!tweakersVal.isArray()) { - QLOG_ERROR() << filename << "contains a 'tweakers' field, but it's not an array"; + QLOG_ERROR() << filename + << "contains a 'tweakers' field, but it's not an array"; return out; } out.shouldOverwriteTweakers = true; @@ -289,7 +290,8 @@ struct VersionFile { if (!tweakerVal.isString()) { - QLOG_ERROR() << filename << "contains a 'tweakers' field entry that's not a string"; + QLOG_ERROR() << filename + << "contains a 'tweakers' field entry that's not a string"; return out; } out.overwriteTweakers.append(tweakerVal.toString()); @@ -300,7 +302,8 @@ struct VersionFile QJsonValue tweakersVal = root.value("+tweakers"); if (!tweakersVal.isArray()) { - QLOG_ERROR() << filename << "contains a '+tweakers' field, but it's not an array"; + QLOG_ERROR() << filename + << "contains a '+tweakers' field, but it's not an array"; return out; } QJsonArray tweakers = root.value("+tweakers").toArray(); @@ -308,7 +311,8 @@ struct VersionFile { if (!tweakerVal.isString()) { - QLOG_ERROR() << filename << "contains a '+tweakers' field entry that's not a string"; + QLOG_ERROR() << filename + << "contains a '+tweakers' field entry that's not a string"; return out; } out.addTweakers.append(tweakerVal.toString()); @@ -319,7 +323,8 @@ struct VersionFile QJsonValue tweakersVal = root.value("-tweakers"); if (!tweakersVal.isArray()) { - QLOG_ERROR() << filename << "contains a '-tweakers' field, but it's not an array"; + QLOG_ERROR() << filename + << "contains a '-tweakers' field, but it's not an array"; return out; } out.shouldOverwriteTweakers = true; @@ -328,7 +333,8 @@ struct VersionFile { if (!tweakerVal.isString()) { - QLOG_ERROR() << filename << "contains a '-tweakers' field entry that's not a string"; + QLOG_ERROR() << filename + << "contains a '-tweakers' field entry that's not a string"; return out; } out.removeTweakers.append(tweakerVal.toString()); @@ -648,7 +654,9 @@ struct VersionFile { const int startOfVersion = lib.name.lastIndexOf(':') + 1; - const int index = findLibrary(version->libraries, QString(lib.name).replace(startOfVersion, INT_MAX, '*')); + const int index = + findLibrary(version->libraries, + QString(lib.name).replace(startOfVersion, INT_MAX, '*')); if (index < 0) { if (lib.insertType == Library::Append) @@ -665,11 +673,14 @@ struct VersionFile auto otherLib = version->libraries.at(index); const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX); const Util::Version otherVersion = otherLib->version(); - // if the existing version is a hard dependency we can either use it or fail, but we can't change it + // if the existing version is a hard dependency we can either use it or + // fail, but we can't change it if (otherLib->dependType == OneSixLibrary::Hard) { - // we need a higher version, or we're hard to and the versions aren't equal - if (ourVersion > otherVersion || (lib.dependType == Library::Hard && ourVersion != otherVersion)) + // we need a higher version, or we're hard to and the versions aren't + // equal + if (ourVersion > otherVersion || + (lib.dependType == Library::Hard && ourVersion != otherVersion)) { QLOG_ERROR() << "Error resolving library dependencies between" << otherLib->rawName() << "and" << lib.name << "in" @@ -695,7 +706,8 @@ struct VersionFile } else { - // our version is smaller than the existing version, but we require it: fail + // our version is smaller than the existing version, but we require + // it: fail if (lib.dependType == Library::Hard) { QLOG_ERROR() << "Error resolving library dependencies between" @@ -753,13 +765,13 @@ OneSixVersionBuilder::OneSixVersionBuilder() } bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, - QWidget *widgetParent, const bool excludeCustom) + QWidget *widgetParent, const bool onlyVanilla) { OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = instance; builder.m_widgetParent = widgetParent; - return builder.build(excludeCustom); + return builder.build(onlyVanilla); } bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) @@ -771,7 +783,7 @@ bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) return builder.read(obj); } -bool OneSixVersionBuilder::build(const bool excludeCustom) +bool OneSixVersionBuilder::build(const bool onlyVanilla) { m_version->clear(); @@ -823,77 +835,79 @@ bool OneSixVersionBuilder::build(const bool excludeCustom) if (isError) { QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("version.json"))); + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("version.json"))); return false; } } - // patches/ + if (!onlyVanilla) { - // load all, put into map for ordering, apply in the right order - QMap> files; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + // patches/ { - QLOG_INFO() << "Reading" << info.fileName(); - VersionFile file; - if (!read(info, true, &file)) + // load all, put into map for ordering, apply in the right order + + QMap> files; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { - return false; + QLOG_INFO() << "Reading" << info.fileName(); + VersionFile file; + if (!read(info, true, &file)) + { + return false; + } + files.insert(file.order, qMakePair(info.fileName(), file)); } - files.insert(file.order, qMakePair(info.fileName(), file)); - } - for (auto order : files.keys()) - { - QLOG_DEBUG() << "Applying file with order" << order; - auto filePair = files[order]; - bool isError = false; - filePair.second.applyTo(m_version, isError); - if (isError) + for (auto order : files.keys()) { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(filePair.first)); - return false; + QLOG_DEBUG() << "Applying file with order" << order; + auto filePair = files[order]; + bool isError = false; + filePair.second.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while applying %1. Please check MultiMC-0.log " + "for more info.").arg(filePair.first)); + return false; + } } } - } #if 0 - // user.json - if (!excludeCustom) - { - if (QFile::exists(root.absoluteFilePath("user.json"))) + // user.json { - QLOG_INFO() << "Reading user.json"; - VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("user.json")), false, &file)) + if (QFile::exists(root.absoluteFilePath("user.json"))) { - return false; - } - file.name = "user.json"; - file.fileId = "org.multimc.user.json"; - file.version = QString(); - file.mcVersion = QString(); - bool isError = false; - file.applyTo(m_version, isError); - if (isError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("user.json"))); - return false; + QLOG_INFO() << "Reading user.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("user.json")), false, &file)) + { + return false; + } + file.name = "user.json"; + file.fileId = "org.multimc.user.json"; + file.version = QString(); + file.mcVersion = QString(); + bool isError = false; + file.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("user.json"))); + return false; + } } } - } #endif + } } // some final touches diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index ac8c13bf..f0d69bf6 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -28,7 +28,7 @@ class OneSixVersionBuilder { OneSixVersionBuilder(); public: - static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool excludeCustom); + static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla); static bool read(OneSixVersion *version, const QJsonObject &obj); private: @@ -36,7 +36,7 @@ private: OneSixInstance *m_instance; QWidget *m_widgetParent; - bool build(const bool excludeCustom); + bool build(const bool onlyVanilla); bool read(const QJsonObject &obj); bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out); From eb0ed082d877156f543324736cbf4ab85a9ec3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 2 Feb 2014 14:27:43 +0100 Subject: [PATCH 33/49] Fix group mouse interaction issues --- Group.cpp | 39 +++++++++++++++++++++++++++---- Group.h | 16 +++++++++++-- GroupView.cpp | 64 ++++++++++++++++++++++++++++++++++++--------------- GroupView.h | 23 +++++++++++------- 4 files changed, 109 insertions(+), 33 deletions(-) diff --git a/Group.cpp b/Group.cpp index a62f592b..c92132ca 100644 --- a/Group.cpp +++ b/Group.cpp @@ -9,9 +9,9 @@ Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) { } + Group::Group(const Group *other) - : view(other->view), text(other->text), collapsed(other->collapsed), - iconRect(other->iconRect), textRect(other->textRect) + : view(other->view), text(other->text), collapsed(other->collapsed) { } @@ -27,6 +27,37 @@ void Group::update() } } +Group::HitResults Group::pointIntersect(const QPoint &pos) const +{ + Group::HitResults results = Group::NoHit; + int y_start = top(); + int body_start = y_start + headerHeight(); + int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? + int y = pos.y(); + // int x = pos.x(); + if(y < y_start) + { + results = Group::NoHit; + } + else if(y < body_start) + { + results = Group::HeaderHit; + int collapseSize = headerHeight() - 4; + + // the icon + QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); + if(iconRect.contains(pos)) + { + results |= Group::CheckboxHit; + } + } + else if (y < body_end) + { + results |= Group::BodyHit; + } + return results; +} + void Group::drawHeader(QPainter *painter, const int y) { painter->save(); @@ -35,7 +66,7 @@ void Group::drawHeader(QPainter *painter, const int y) int collapseSize = height; // the icon - iconRect = QRect(view->m_rightMargin + 2, 2 + y, collapseSize, collapseSize); + QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y, collapseSize, collapseSize); painter->setPen(QPen(Qt::black, 1)); painter->drawRect(iconRect); static const int margin = 2; @@ -50,7 +81,7 @@ void Group::drawHeader(QPainter *painter, const int y) // the text int textWidth = painter->fontMetrics().width(text); - textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); + QRect textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); painter->setBrush(view->viewOptions().palette.text()); view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, view->viewport()->palette(), true, text); diff --git a/Group.h b/Group.h index 6a8fadeb..51e0470d 100644 --- a/Group.h +++ b/Group.h @@ -15,8 +15,6 @@ struct Group GroupView *view; QString text; bool collapsed; - QRect iconRect; - QRect textRect; QVector rowHeights; int firstRow; @@ -29,8 +27,22 @@ struct Group int numRows() const; int top() const; + enum HitResult + { + NoHit = 0x0, + TextHit = 0x1, + CheckboxHit = 0x2, + HeaderHit = 0x4, + BodyHit = 0x8 + }; + Q_DECLARE_FLAGS(HitResults, HitResult) + + HitResults pointIntersect (const QPoint &pos) const; + QList items() const; int numItems() const; QModelIndex firstItem() const; QModelIndex lastItem() const; }; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults) diff --git a/GroupView.cpp b/GroupView.cpp index 50d19f52..1f0a51e7 100644 --- a/GroupView.cpp +++ b/GroupView.cpp @@ -36,7 +36,7 @@ GroupView::GroupView(QWidget *parent) // setWordWrap(true); // setDragDropMode(QListView::InternalMove); setAcceptDrops(true); - // setSpacing(10); + m_spacing = 5; } GroupView::~GroupView() @@ -145,31 +145,31 @@ Group *GroupView::category(const QModelIndex &index) const Group *GroupView::category(const QString &cat) const { - for (int i = 0; i < m_categories.size(); ++i) + for (auto group : m_categories) { - if (m_categories.at(i)->text == cat) + if (group->text == cat) { - return m_categories.at(i); + return group; } } - return 0; + return nullptr; } Group *GroupView::categoryAt(const QPoint &pos) const { - for (int i = 0; i < m_categories.size(); ++i) + for (auto group : m_categories) { - if (m_categories.at(i)->iconRect.contains(pos)) + if(group->pointIntersect(pos) & Group::CheckboxHit) { - return m_categories.at(i); + return group; } } - return 0; + return nullptr; } int GroupView::itemsPerRow() const { - return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + /* spacing */ 10)); + return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing)); } int GroupView::contentWidth() const @@ -261,7 +261,7 @@ void GroupView::mousePressEvent(QMouseEvent *event) m_pressedIndex = index; m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); - QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); + QItemSelectionModel::SelectionFlags selection_flags = selectionCommand(index, event); m_pressedPosition = pos; m_pressedCategory = categoryAt(m_pressedPosition); @@ -428,9 +428,8 @@ void GroupView::mouseDoubleClickEvent(QMouseEvent *event) void GroupView::paintEvent(QPaintEvent *event) { QPainter painter(this->viewport()); - painter.translate(-offset()); - int y = 0; + int y = -verticalOffset(); for (int i = 0; i < m_categories.size(); ++i) { Group *category = m_categories.at(i); @@ -502,7 +501,7 @@ void GroupView::resizeEvent(QResizeEvent *event) // if (m_categoryEditor) // { // m_categoryEditor->resize(qMax(contentWidth() / 2, - //m_editedCategory->textRect.width()), + // m_editedCategory->textRect.width()), // m_categoryEditor->height()); // } @@ -618,6 +617,11 @@ void GroupView::startDrag(Qt::DropActions supportedActions) } QRect GroupView::visualRect(const QModelIndex &index) const +{ + return geometryRect(index).translated(-offset()); +} + +QRect GroupView::geometryRect(const QModelIndex &index) const { if (!index.isValid() || isIndexHidden(index) || index.column() > 0) { @@ -627,15 +631,16 @@ QRect GroupView::visualRect(const QModelIndex &index) const const Group *cat = category(index); QPair pos = categoryInternalPosition(index); int x = pos.first; - int y = pos.second; + // int y = pos.second; QRect out; out.setTop(cat->top() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); - out.setLeft(/*spacing*/ 10 + x * itemWidth() + x * /*spacing()*/ 10); + out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); return out; } + /* void CategorizedView::startCategoryEditor(Category *category) { @@ -680,7 +685,7 @@ QModelIndex GroupView::indexAt(const QPoint &point) const for (int i = 0; i < model()->rowCount(); ++i) { QModelIndex index = model()->index(i, 0); - if (visualRect(index).contains(point)) + if (geometryRect(index).contains(point)) { return index; } @@ -694,7 +699,7 @@ void GroupView::setSelection(const QRect &rect, for (int i = 0; i < model()->rowCount(); ++i) { QModelIndex index = model()->index(i, 0); - if (visualRect(index).intersects(rect)) + if (geometryRect(index).intersects(rect)) { selectionModel()->select(index, commands); } @@ -734,7 +739,7 @@ QList> GroupView::draggablePaintPairs(const QModelInde for (int i = 0; i < indices.count(); ++i) { const QModelIndex &index = indices.at(i); - const QRect current = visualRect(index); + const QRect current = geometryRect(index); if (current.intersects(viewportRect)) { ret += qMakePair(current, index); @@ -857,3 +862,24 @@ QPoint GroupView::offset() const { return QPoint(horizontalOffset(), verticalOffset()); } + +QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) const +{ + QRegion region; + for (auto &range : selection) + { + int start_row = range.top(); + int end_row = range.bottom(); + for (int row = start_row; row <= end_row; ++row) + { + int start_column = range.left(); + int end_column = range.right(); + for (int column = start_column; column <= end_column; ++column) + { + QModelIndex index = model()->index(row, column, rootIndex()); + region += visualRect(index); // OK + } + } + } + return region; +} diff --git a/GroupView.h b/GroupView.h index bf911794..2c60f0e9 100644 --- a/GroupView.h +++ b/GroupView.h @@ -2,6 +2,7 @@ #include #include +#include struct CategorizedViewRoles { @@ -23,7 +24,8 @@ public: GroupView(QWidget *parent = 0); ~GroupView(); - virtual QRect visualRect(const QModelIndex &index) const; + virtual QRect geometryRect(const QModelIndex &index) const; + virtual QRect visualRect(const QModelIndex &index) const override; QModelIndex indexAt(const QPoint &point) const; void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; @@ -34,12 +36,18 @@ public: virtual int horizontalOffset() const override { - return 0; + return horizontalScrollBar()->value(); } virtual int verticalOffset() const override { - return 0; + return verticalScrollBar()->value(); + } + + virtual void scrollContentsBy(int dx, int dy) override + { + scrollDirtyRegion(dx, dy); + viewport()->scroll(dx, dy); } virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override @@ -53,10 +61,7 @@ public: return QModelIndex(); } - virtual QRegion visualRegionForSelection(const QItemSelection &) const override - { - return QRegion(); - } + virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; /* * End of BS @@ -116,14 +121,16 @@ private: private slots: void endCategoryEditor();*/ -private: +private: /* variables */ QPoint m_pressedPosition; QPersistentModelIndex m_pressedIndex; bool m_pressedAlreadySelected; Group *m_pressedCategory; QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; QPoint m_lastDragPosition; + int m_spacing = 5; +private: /* methods */ QPair categoryInternalPosition(const QModelIndex &index) const; int categoryInternalRowTop(const QModelIndex &index) const; int itemHeightForCategoryRow(const Group *category, const int internalRow) const; From 2cd9b0647640ee595113ceca128db1131bebc19a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 2 Feb 2014 16:57:00 +0100 Subject: [PATCH 34/49] Fix drag&drop pixmap rendering --- GroupView.cpp | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/GroupView.cpp b/GroupView.cpp index 1f0a51e7..b47cf93f 100644 --- a/GroupView.cpp +++ b/GroupView.cpp @@ -296,7 +296,7 @@ void GroupView::mousePressEvent(QMouseEvent *event) void GroupView::mouseMoveEvent(QMouseEvent *event) { QPoint topLeft; - QPoint bottomRight = event->pos(); + QPoint pos = event->pos() + offset(); if (state() == ExpandingState || state() == CollapsingState) { @@ -316,15 +316,13 @@ void GroupView::mouseMoveEvent(QMouseEvent *event) return; } - QPersistentModelIndex index = indexAt(bottomRight); - if (selectionMode() != SingleSelection) { topLeft = m_pressedPosition - offset(); } else { - topLeft = bottomRight; + topLeft = pos; } if (m_pressedIndex.isValid() && (state() != DragSelectingState) && @@ -337,17 +335,9 @@ void GroupView::mouseMoveEvent(QMouseEvent *event) if ((event->buttons() & Qt::LeftButton) && selectionModel()) { setState(DragSelectingState); - QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); - if (m_ctrlDragSelectionFlag != QItemSelectionModel::NoUpdate && - command.testFlag(QItemSelectionModel::Toggle)) - { - command &= ~QItemSelectionModel::Toggle; - command |= m_ctrlDragSelectionFlag; - } - // Do the normalize ourselves, since QRect::normalized() is flawed - QRect selectionRect = QRect(topLeft, bottomRight); - setSelection(selectionRect, command); + setSelection(QRect(pos, pos), QItemSelectionModel::ClearAndSelect); + QModelIndex index = indexAt(pos); // set at the end because it might scroll the view if (index.isValid() && (index != selectionModel()->currentIndex()) && @@ -582,7 +572,7 @@ void GroupView::startDrag(Qt::DropActions supportedActions) } QRect rect; QPixmap pixmap = renderToPixmap(indexes, &rect); - rect.translate(offset()); + //rect.translate(offset()); // rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); @@ -699,12 +689,14 @@ void GroupView::setSelection(const QRect &rect, for (int i = 0; i < model()->rowCount(); ++i) { QModelIndex index = model()->index(i, 0); - if (geometryRect(index).intersects(rect)) + QRect itemRect = geometryRect(index); + if (itemRect.intersects(rect)) { selectionModel()->select(index, commands); + update(itemRect.translated(-offset())); } } - update(); + } QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const @@ -725,6 +717,7 @@ QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) cons option.rect = paintPairs.at(j).first.translated(-r->topLeft()); const QModelIndex ¤t = paintPairs.at(j).second; itemDelegate()->paint(&painter, option, current); + painter.drawLine(0,0, r->width(), r->height()); } return pixmap; } @@ -740,13 +733,13 @@ QList> GroupView::draggablePaintPairs(const QModelInde { const QModelIndex &index = indices.at(i); const QRect current = geometryRect(index); - if (current.intersects(viewportRect)) - { + //if (current.intersects(viewportRect)) + //{ ret += qMakePair(current, index); rect |= current; - } + //} } - rect &= viewportRect; + //rect &= viewportRect; return ret; } From ddedd077b66f2bf45cd157d760eebdad15bcb289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 2 Feb 2014 18:30:52 +0100 Subject: [PATCH 35/49] Oh my. --- GroupView.cpp | 40 +++++++++++++++++++++++++++++++++------- GroupView.h | 15 ++++----------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/GroupView.cpp b/GroupView.cpp index b47cf93f..3296ccfb 100644 --- a/GroupView.cpp +++ b/GroupView.cpp @@ -727,19 +727,14 @@ QList> GroupView::draggablePaintPairs(const QModelInde { Q_ASSERT(r); QRect &rect = *r; - const QRect viewportRect = viewport()->rect(); QList> ret; for (int i = 0; i < indices.count(); ++i) { const QModelIndex &index = indices.at(i); const QRect current = geometryRect(index); - //if (current.intersects(viewportRect)) - //{ - ret += qMakePair(current, index); - rect |= current; - //} + ret += qMakePair(current, index); + rect |= current; } - //rect &= viewportRect; return ret; } @@ -876,3 +871,34 @@ QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) con } return region; } +QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction, + Qt::KeyboardModifiers modifiers) +{ + auto current = currentIndex(); + if(!current.isValid()) + { + qDebug() << "model row: invalid"; + return current; + } + qDebug() << "model row: " << current.row(); + auto cat = category(current); + int i = m_categories.indexOf(cat); + if(i >= 0) + { + // this is a pile of something foul + auto real_cat = m_categories[i]; + int beginning_row = 0; + for(auto catt: m_categories) + { + if(catt == real_cat) + break; + beginning_row += catt->numRows(); + } + qDebug() << "category: " << real_cat->text; + QPair pos = categoryInternalPosition(current); + int row = beginning_row + pos.second; + qDebug() << "row: " << row; + qDebug() << "column: " << pos.first; + } + return current; +} diff --git a/GroupView.h b/GroupView.h index 2c60f0e9..a7ed8e9d 100644 --- a/GroupView.h +++ b/GroupView.h @@ -30,10 +30,6 @@ public: void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; - /* - * BS - */ - virtual int horizontalOffset() const override { return horizontalScrollBar()->value(); @@ -50,22 +46,19 @@ public: viewport()->scroll(dx, dy); } + /* + * TODO! + */ virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override { return; } virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) - override - { - return QModelIndex(); - } + override; virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; - /* - * End of BS - */ protected slots: From ac2f64f3373f37e4ba039d4395857f59ca7975af Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 3 Feb 2014 20:42:04 +0100 Subject: [PATCH 36/49] Reload version after removing custom.json --- gui/dialogs/OneSixModEditDialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index 41ac5d8d..1b61771d 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -138,6 +138,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked() return; } QDir(m_inst->instanceRoot()).remove("custom.json"); + m_inst->reloadVersion(this); } VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this); vselect.setFilter(1, m_inst->currentVersionId()); @@ -194,6 +195,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked() return; } QDir(m_inst->instanceRoot()).remove("custom.json"); + m_inst->reloadVersion(this); } LiteLoaderInstaller liteloader; if (!liteloader.canApply(m_inst)) From 946d49675cb4725c31ab49a51f3bcae302f89a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 4 Feb 2014 00:40:10 +0100 Subject: [PATCH 37/49] Swap things around -- rename refactors, moving towards non-derpy design. Maybe. --- Group.cpp | 16 +++++----- Group.h | 25 ++++++++++++++-- GroupView.cpp | 68 +++++++++++++++++++++++-------------------- GroupView.h | 25 ++++++++-------- GroupedProxyModel.cpp | 4 +-- InstanceDelegate.cpp | 4 +-- main.cpp | 4 +-- main.h | 8 ++--- 8 files changed, 88 insertions(+), 66 deletions(-) diff --git a/Group.cpp b/Group.cpp index c92132ca..f216cc6e 100644 --- a/Group.cpp +++ b/Group.cpp @@ -17,20 +17,20 @@ Group::Group(const Group *other) void Group::update() { - firstRow = firstItem().row(); + firstItemIndex = firstItem().row(); rowHeights = QVector(numRows()); for (int i = 0; i < numRows(); ++i) { rowHeights[i] = view->categoryRowHeight( - view->model()->index(i * view->itemsPerRow() + firstRow, 0)); + view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0)); } } -Group::HitResults Group::pointIntersect(const QPoint &pos) const +Group::HitResults Group::hitScan(const QPoint &pos) const { Group::HitResults results = Group::NoHit; - int y_start = top(); + int y_start = verticalPosition(); int body_start = y_start + headerHeight(); int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? int y = pos.y(); @@ -95,7 +95,7 @@ void Group::drawHeader(QPainter *painter, const int y) int Group::totalHeight() const { - return headerHeight() + 5 + contentHeight(); + return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'? } int Group::headerHeight() const @@ -122,10 +122,10 @@ int Group::numRows() const return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow())); } -int Group::top() const +int Group::verticalPosition() const { int res = 0; - const QList cats = view->m_categories; + const QList cats = view->m_groups; for (int i = 0; i < cats.size(); ++i) { if (cats.at(i) == this) @@ -143,7 +143,7 @@ QList Group::items() const for (int i = 0; i < view->model()->rowCount(); ++i) { const QModelIndex index = view->model()->index(i, 0); - if (index.data(CategorizedViewRoles::CategoryRole).toString() == text) + if (index.data(GroupViewRoles::GroupRole).toString() == text) { indices.append(index); } diff --git a/Group.h b/Group.h index 51e0470d..455ee1a8 100644 --- a/Group.h +++ b/Group.h @@ -10,22 +10,38 @@ class QModelIndex; struct Group { +/* constructors */ Group(const QString &text, GroupView *view); Group(const Group *other); + +/* data */ GroupView *view; QString text; bool collapsed; QVector rowHeights; - int firstRow; + int firstItemIndex; +/* logic */ + /// do stuff. and things. TODO: redo. void update(); + /// draw the header at y-position. void drawHeader(QPainter *painter, const int y); + + /// height of the group, in total. includes a small bit of padding. int totalHeight() const; + + /// height of the group header, in pixels int headerHeight() const; + + /// height of the group content, in pixels int contentHeight() const; + + /// the number of visual rows this group has int numRows() const; - int top() const; + + /// the height at which this group starts, in pixels + int verticalPosition() const; enum HitResult { @@ -37,9 +53,12 @@ struct Group }; Q_DECLARE_FLAGS(HitResults, HitResult) - HitResults pointIntersect (const QPoint &pos) const; + /// shoot! BANG! what did we hit? + HitResults hitScan (const QPoint &pos) const; + /// super derpy thing. QList items() const; + /// I don't even int numItems() const; QModelIndex firstItem() const; QModelIndex lastItem() const; diff --git a/GroupView.cpp b/GroupView.cpp index 3296ccfb..89e3e223 100644 --- a/GroupView.cpp +++ b/GroupView.cpp @@ -41,14 +41,14 @@ GroupView::GroupView(QWidget *parent) GroupView::~GroupView() { - qDeleteAll(m_categories); - m_categories.clear(); + qDeleteAll(m_groups); + m_groups.clear(); } void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { - if (roles.contains(CategorizedViewRoles::CategoryRole) || roles.contains(Qt::DisplayRole)) + if (roles.contains(GroupViewRoles::GroupRole) || roles.contains(Qt::DisplayRole)) { updateGeometries(); } @@ -74,18 +74,18 @@ void GroupView::updateGeometries() for (int i = 0; i < model()->rowCount(); ++i) { - const QString category = - model()->index(i, 0).data(CategorizedViewRoles::CategoryRole).toString(); - if (!cats.contains(category)) + const QString groupName = + model()->index(i, 0).data(GroupViewRoles::GroupRole).toString(); + if (!cats.contains(groupName)) { - Group *old = this->category(category); + Group *old = this->category(groupName); if (old) { - cats.insert(category, new Group(old)); + cats.insert(groupName, new Group(old)); } else { - cats.insert(category, new Group(category, this)); + cats.insert(groupName, new Group(groupName, this)); } } } @@ -95,22 +95,22 @@ void GroupView::updateGeometries() m_editedCategory = cats[m_editedCategory->text]; }*/ - qDeleteAll(m_categories); - m_categories = cats.values(); + qDeleteAll(m_groups); + m_groups = cats.values(); - for (auto cat : m_categories) + for (auto cat : m_groups) { cat->update(); } - if (m_categories.isEmpty()) + if (m_groups.isEmpty()) { verticalScrollBar()->setRange(0, 0); } else { int totalHeight = 0; - for (auto category : m_categories) + for (auto category : m_groups) { totalHeight += category->totalHeight() + m_categoryMargin; } @@ -140,12 +140,12 @@ bool GroupView::isIndexHidden(const QModelIndex &index) const Group *GroupView::category(const QModelIndex &index) const { - return category(index.data(CategorizedViewRoles::CategoryRole).toString()); + return category(index.data(GroupViewRoles::GroupRole).toString()); } Group *GroupView::category(const QString &cat) const { - for (auto group : m_categories) + for (auto group : m_groups) { if (group->text == cat) { @@ -157,9 +157,9 @@ Group *GroupView::category(const QString &cat) const Group *GroupView::categoryAt(const QPoint &pos) const { - for (auto group : m_categories) + for (auto group : m_groups) { - if(group->pointIntersect(pos) & Group::CheckboxHit) + if(group->hitScan(pos) & Group::CheckboxHit) { return group; } @@ -420,9 +420,9 @@ void GroupView::paintEvent(QPaintEvent *event) QPainter painter(this->viewport()); int y = -verticalOffset(); - for (int i = 0; i < m_categories.size(); ++i) + for (int i = 0; i < m_groups.size(); ++i) { - Group *category = m_categories.at(i); + Group *category = m_groups.at(i); category->drawHeader(&painter, y); y += category->totalHeight() + m_categoryMargin; } @@ -457,6 +457,10 @@ void GroupView::paintEvent(QPaintEvent *event) itemDelegate()->paint(&painter, option, index); } + /* + * Drop indicators for manual reordering... + */ +#if 0 if (!m_lastDragPosition.isNull()) { QPair pair = rowDropPos(m_lastDragPosition); @@ -464,7 +468,7 @@ void GroupView::paintEvent(QPaintEvent *event) int row = pair.second; if (category) { - int internalRow = row - category->firstRow; + int internalRow = row - category->firstItemIndex; QLine line; if (internalRow >= category->numItems()) { @@ -482,6 +486,7 @@ void GroupView::paintEvent(QPaintEvent *event) painter.restore(); } } +#endif } void GroupView::resizeEvent(QResizeEvent *event) @@ -552,7 +557,7 @@ void GroupView::dropEvent(QDropEvent *event) if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) { model()->setData(model()->index(row, 0), categoryText, - CategorizedViewRoles::CategoryRole); + GroupViewRoles::GroupRole); event->setDropAction(Qt::MoveAction); event->accept(); } @@ -624,7 +629,7 @@ QRect GroupView::geometryRect(const QModelIndex &index) const // int y = pos.second; QRect out; - out.setTop(cat->top() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); + out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); @@ -717,7 +722,6 @@ QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) cons option.rect = paintPairs.at(j).first.translated(-r->topLeft()); const QModelIndex ¤t = paintPairs.at(j).second; itemDelegate()->paint(&painter, option, current); - painter.drawLine(0,0, r->width(), r->height()); } return pixmap; } @@ -762,7 +766,7 @@ QPair GroupView::rowDropPos(const QPoint &pos) Group *category = 0; { int y = 0; - for (auto cat : m_categories) + for (auto cat : m_groups) { if (pos.y() > y && pos.y() < (y + cat->headerHeight())) { @@ -812,7 +816,7 @@ QPair GroupView::rowDropPos(const QPoint &pos) int internalRow = -1; { // FIXME rework the drag and drop code - const int top = category->top(); + const int top = category->verticalPosition(); for (int r = 0, h = top; r < category->numRows(); h += itemHeightForCategoryRow(category, r), ++r) { @@ -882,19 +886,19 @@ QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction, } qDebug() << "model row: " << current.row(); auto cat = category(current); - int i = m_categories.indexOf(cat); + int i = m_groups.indexOf(cat); if(i >= 0) { // this is a pile of something foul - auto real_cat = m_categories[i]; + auto real_group = m_groups[i]; int beginning_row = 0; - for(auto catt: m_categories) + for(auto group: m_groups) { - if(catt == real_cat) + if(group == real_group) break; - beginning_row += catt->numRows(); + beginning_row += group->numRows(); } - qDebug() << "category: " << real_cat->text; + qDebug() << "category: " << real_group->text; QPair pos = categoryInternalPosition(current); int row = beginning_row + pos.second; qDebug() << "row: " << row; diff --git a/GroupView.h b/GroupView.h index a7ed8e9d..329a3503 100644 --- a/GroupView.h +++ b/GroupView.h @@ -4,11 +4,11 @@ #include #include -struct CategorizedViewRoles +struct GroupViewRoles { enum { - CategoryRole = Qt::UserRole, + GroupRole = Qt::UserRole, ProgressValueRole, ProgressMaximumRole }; @@ -24,7 +24,7 @@ public: GroupView(QWidget *parent = 0); ~GroupView(); - virtual QRect geometryRect(const QModelIndex &index) const; + QRect geometryRect(const QModelIndex &index) const; virtual QRect visualRect(const QModelIndex &index) const override; QModelIndex indexAt(const QPoint &point) const; void setSelection(const QRect &rect, @@ -54,22 +54,21 @@ public: return; } - virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) - override; + virtual QModelIndex moveCursor(CursorAction cursorAction, + Qt::KeyboardModifiers modifiers) override; virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; - protected slots: - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVector &roles); - virtual void rowsInserted(const QModelIndex &parent, int start, int end); - virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - virtual void updateGeometries(); + virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QVector &roles) override; + virtual void rowsInserted(const QModelIndex &parent, int start, int end) override; + virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override; + virtual void updateGeometries() override; protected: - virtual bool isIndexHidden(const QModelIndex &index) const; + virtual bool isIndexHidden(const QModelIndex &index) const override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; @@ -87,7 +86,7 @@ protected: private: friend struct Group; - QList m_categories; + QList m_groups; int m_leftMargin; int m_rightMargin; diff --git a/GroupedProxyModel.cpp b/GroupedProxyModel.cpp index ab00a412..57d7ff5c 100644 --- a/GroupedProxyModel.cpp +++ b/GroupedProxyModel.cpp @@ -8,8 +8,8 @@ GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(pa bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - const QString leftCategory = left.data(CategorizedViewRoles::CategoryRole).toString(); - const QString rightCategory = right.data(CategorizedViewRoles::CategoryRole).toString(); + const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString(); + const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString(); if (leftCategory == rightCategory) { return left.row() < right.row(); diff --git a/InstanceDelegate.cpp b/InstanceDelegate.cpp index 944cfd76..8527e2bc 100644 --- a/InstanceDelegate.cpp +++ b/InstanceDelegate.cpp @@ -253,8 +253,8 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti } drawProgressOverlay(painter, opt, - index.data(CategorizedViewRoles::ProgressValueRole).toInt(), - index.data(CategorizedViewRoles::ProgressMaximumRole).toInt()); + index.data(GroupViewRoles::ProgressValueRole).toInt(), + index.data(GroupViewRoles::ProgressMaximumRole).toInt()); painter->restore(); } diff --git a/main.cpp b/main.cpp index b9ecde8f..df14e3bd 100644 --- a/main.cpp +++ b/main.cpp @@ -36,7 +36,7 @@ QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, QStandardItem *item = new QStandardItem; item->setText(text); item->setData(icon(color), Qt::DecorationRole); - item->setData(category, CategorizedViewRoles::CategoryRole); + item->setData(category, GroupViewRoles::GroupRole); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // progresser->addTrackedIndex(item); return item; @@ -46,7 +46,7 @@ QStandardItem *createItem(const int index, const QString &category) QStandardItem *item = new QStandardItem; item->setText(QString("Item #%1").arg(index)); item->setData(icon(index), Qt::DecorationRole); - item->setData(category, CategorizedViewRoles::CategoryRole); + item->setData(category, GroupViewRoles::GroupRole); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // progresser->addTrackedIndex(item); return item; diff --git a/main.h b/main.h index 6883e98e..2a358329 100644 --- a/main.h +++ b/main.h @@ -21,7 +21,7 @@ public: QStandardItem *addTrackedIndex(QStandardItem *item) { - item->setData(1000, CategorizedViewRoles::ProgressMaximumRole); + item->setData(1000, GroupViewRoles::ProgressMaximumRole); m_items.append(item); return item; } @@ -33,10 +33,10 @@ slots: QList toRemove; for (auto item : m_items) { - int maximum = item->data(CategorizedViewRoles::ProgressMaximumRole).toInt(); - int value = item->data(CategorizedViewRoles::ProgressValueRole).toInt(); + int maximum = item->data(GroupViewRoles::ProgressMaximumRole).toInt(); + int value = item->data(GroupViewRoles::ProgressValueRole).toInt(); int newvalue = std::min(value + 3, maximum); - item->setData(newvalue, CategorizedViewRoles::ProgressValueRole); + item->setData(newvalue, GroupViewRoles::ProgressValueRole); if(newvalue >= maximum) { From a17caba2c9c2aa5960581db01e4b66472a9c019c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 4 Feb 2014 00:43:18 +0100 Subject: [PATCH 38/49] Nuke. --- depends/groupview/CMakeLists.txt | 46 - .../include/categorizedsortfilterproxymodel.h | 175 -- depends/groupview/include/categorizedview.h | 332 ---- depends/groupview/include/categorydrawer.h | 179 -- depends/groupview/include/groupview_config.h | 28 - .../src/categorizedsortfilterproxymodel.cpp | 168 -- .../src/categorizedsortfilterproxymodel_p.h | 48 - depends/groupview/src/categorizedview.cpp | 1692 ----------------- depends/groupview/src/categorizedview_p.h | 160 -- depends/groupview/src/categorydrawer.cpp | 231 --- 10 files changed, 3059 deletions(-) delete mode 100644 depends/groupview/CMakeLists.txt delete mode 100644 depends/groupview/include/categorizedsortfilterproxymodel.h delete mode 100644 depends/groupview/include/categorizedview.h delete mode 100644 depends/groupview/include/categorydrawer.h delete mode 100644 depends/groupview/include/groupview_config.h delete mode 100644 depends/groupview/src/categorizedsortfilterproxymodel.cpp delete mode 100644 depends/groupview/src/categorizedsortfilterproxymodel_p.h delete mode 100644 depends/groupview/src/categorizedview.cpp delete mode 100644 depends/groupview/src/categorizedview_p.h delete mode 100644 depends/groupview/src/categorydrawer.cpp diff --git a/depends/groupview/CMakeLists.txt b/depends/groupview/CMakeLists.txt deleted file mode 100644 index 417b1d3a..00000000 --- a/depends/groupview/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -project(libGroupView) - -set(CMAKE_AUTOMOC ON) - -# Find Qt -find_package(Qt5Core REQUIRED) -find_package(Qt5Widgets REQUIRED) - -# Include Qt headers. -include_directories(${Qt5Base_INCLUDE_DIRS}) - -SET(LIBGROUPVIEW_HEADERS -include/groupview_config.h - -# Public headers -include/categorizedsortfilterproxymodel.h -include/categorizedview.h -include/categorydrawer.h - -# Private headers -src/categorizedsortfilterproxymodel_p.h -src/categorizedview_p.h -) - -SET(LIBGROUPVIEW_SOURCES -src/categorizedsortfilterproxymodel.cpp -src/categorizedview.cpp -src/categorydrawer.cpp -) - -# Set the include dir path. -SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) - -# Include self. -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories(${CMAKE_BINARY_DIR}/include) - -# Static link! -ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC) - -add_definitions(-DLIBGROUPVIEW_LIBRARY) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -add_library(libGroupView STATIC ${LIBGROUPVIEW_SOURCES} ${LIBGROUPVIEW_HEADERS}) -qt5_use_modules(libGroupView Core Widgets) diff --git a/depends/groupview/include/categorizedsortfilterproxymodel.h b/depends/groupview/include/categorizedsortfilterproxymodel.h deleted file mode 100644 index d90fb254..00000000 --- a/depends/groupview/include/categorizedsortfilterproxymodel.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López - * Copyright (C) 2007 John Tapsell - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_H -#define KCATEGORIZEDSORTFILTERPROXYMODEL_H - -#include - -#include - -class QItemSelection; - - -/** - * This class lets you categorize a view. It is meant to be used along with - * KCategorizedView class. - * - * In general terms all you need to do is to reimplement subSortLessThan() and - * compareCategories() methods. In order to make categorization work, you need - * to also call setCategorizedModel() class to enable it, since the categorization - * is disabled by default. - * - * @see KCategorizedView - * - * @author Rafael Fernández López - */ -class LIBGROUPVIEW_EXPORT KCategorizedSortFilterProxyModel - : public QSortFilterProxyModel -{ -public: - enum AdditionalRoles - { - // Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM)) - // to define additional roles. - CategoryDisplayRole = 0x17CE990A, ///< This role is used for asking the category to a given index - - CategorySortRole = 0x27857E60 ///< This role is used for sorting categories. You can return a - ///< string or a long long value. Strings will be sorted alphabetically - ///< while long long will be sorted by their value. Please note that this - ///< value won't be shown on the view, is only for sorting purposes. What will - ///< be shown as "Category" on the view will be asked with the role - ///< CategoryDisplayRole. - }; - - KCategorizedSortFilterProxyModel ( QObject *parent = 0 ); - virtual ~KCategorizedSortFilterProxyModel(); - - /** - * Overridden from QSortFilterProxyModel. Sorts the source model using - * @p column for the given @p order. - */ - virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder ); - - /** - * @return whether the model is categorized or not. Disabled by default. - */ - bool isCategorizedModel() const; - - /** - * Enables or disables the categorization feature. - * - * @param categorizedModel whether to enable or disable the categorization feature. - */ - void setCategorizedModel ( bool categorizedModel ); - - /** - * @return the column being used for sorting. - */ - int sortColumn() const; - - /** - * @return the sort order being used for sorting. - */ - Qt::SortOrder sortOrder() const; - - /** - * Set if the sorting using CategorySortRole will use a natural comparison - * in the case that strings were returned. If enabled, QString::localeAwareCompare - * will be used for sorting. - * - * @param sortCategoriesByNaturalComparison whether to sort using a natural comparison or not. - */ - void setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison ); - - /** - * @return whether it is being used a natural comparison for sorting. Enabled by default. - */ - bool sortCategoriesByNaturalComparison() const; - -protected: - /** - * Overridden from QSortFilterProxyModel. If you are subclassing - * KCategorizedSortFilterProxyModel, you will probably not need to reimplement this - * method. - * - * It calls compareCategories() to sort by category. If the both items are in the - * same category (i.e. compareCategories returns 0), then subSortLessThan is called. - * - * @return Returns true if the item @p left is less than the item @p right when sorting. - * - * @warning You usually won't need to reimplement this method when subclassing - * from KCategorizedSortFilterProxyModel. - */ - virtual bool lessThan ( const QModelIndex &left, const QModelIndex &right ) const; - - /** - * This method has a similar purpose as lessThan() has on QSortFilterProxyModel. - * It is used for sorting items that are in the same category. - * - * @return Returns true if the item @p left is less than the item @p right when sorting. - */ - virtual bool subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const; - - /** - * This method compares the category of the @p left index with the category - * of the @p right index. - * - * Internally and if not reimplemented, this method will ask for @p left and - * @p right models for role CategorySortRole. In order to correctly sort - * categories, the data() metod of the model should return a qlonglong (or numeric) value, or - * a QString object. QString objects will be sorted with QString::localeAwareCompare if - * sortCategoriesByNaturalComparison() is true. - * - * @note Please have present that: - * QString(QChar(QChar::ObjectReplacementCharacter)) > - * QString(QChar(QChar::ReplacementCharacter)) > - * [ all possible strings ] > - * QString(); - * - * This means that QString() will be sorted the first one, while - * QString(QChar(QChar::ObjectReplacementCharacter)) and - * QString(QChar(QChar::ReplacementCharacter)) will be sorted in last - * position. - * - * @warning Please note that data() method of the model should return always - * information of the same type. If you return a QString for an index, - * you should return always QStrings for all indexes for role CategorySortRole - * in order to correctly sort categories. You can't mix by returning - * a QString for one index, and a qlonglong for other. - * - * @note If you need a more complex layout, you will have to reimplement this - * method. - * - * @return A negative value if the category of @p left should be placed before the - * category of @p right. 0 if @p left and @p right are on the same category, and - * a positive value if the category of @p left should be placed after the - * category of @p right. - */ - virtual int compareCategories ( const QModelIndex &left, const QModelIndex &right ) const; - -private: - class Private; - Private *const d; -}; - - -#endif // KCATEGORIZEDSORTFILTERPROXYMODEL_H diff --git a/depends/groupview/include/categorizedview.h b/depends/groupview/include/categorizedview.h deleted file mode 100644 index 81b1dbb1..00000000 --- a/depends/groupview/include/categorizedview.h +++ /dev/null @@ -1,332 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KCATEGORIZEDVIEW_H -#define KCATEGORIZEDVIEW_H - -#include - -#include - -class KCategoryDrawer; - -/** - * @short Item view for listing items in a categorized fashion optionally - * - * KCategorizedView basically has the same functionality as QListView, only that it also lets you - * layout items in a way that they are categorized visually. - * - * For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer - * with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be - * flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true). - * - * The way it works (if categorization enabled): - * - * - When sorting, it does more things than QListView does. It will ask the model for the - * special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return - * a QString or an int in order to tell the view the order of categories. In this sense, for - * instance, if we are sorting by name ascending, "A" would be before than "B". If we are - * sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are - * also sorted. - * - * - When the view has to paint, it will ask the model with the role CategoryDisplayRole - * (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if - * we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example. - * - * For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own - * drawing. - * - * @note All examples cited before talk about filesystems and such, but have present that this - * is a completely generic class, and it can be used for whatever your purpose is. For - * instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In - * this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the - * same ("Mammal" and "Oviparous"). - * - * @note There is a really performance boost if CategorySortRole returns an int instead of a QString. - * Have present that this role is asked (n * log n) times when sorting and compared. Comparing - * ints is always faster than comparing strings, whithout mattering how fast the string - * comparison is. Consider thinking of a way of returning ints instead of QStrings if your - * model can contain a high number of items. - * - * @warning Note that for really drawing items in blocks you will need some things to be done: - * - The model set to this view has to be (or inherit if you want to do special stuff - * in it) KCategorizedSortFilterProxyModel. - * - This model needs to be set setCategorizedModel to true. - * - Set a category drawer by calling setCategoryDrawer. - * - * @see KCategorizedSortFilterProxyModel, KCategoryDrawer - * - * @author Rafael Fernández López - */ -class LIBGROUPVIEW_EXPORT KCategorizedView - : public QListView -{ - Q_OBJECT - Q_PROPERTY ( int categorySpacing READ categorySpacing WRITE setCategorySpacing ) - Q_PROPERTY ( bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors ) - Q_PROPERTY ( bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks ) - -public: - KCategorizedView ( QWidget *parent = 0 ); - - ~KCategorizedView(); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void setModel ( QAbstractItemModel *model ); - - /** - * Calls to setGridSizeOwn(). - */ - void setGridSize ( const QSize &size ); - - /** - * @warning note that setGridSize is not virtual in the base class (QListView), so if you are - * calling to this method, make sure you have a KCategorizedView pointer around. This - * means that something like: - * @code - * QListView *lv = new KCategorizedView(); - * lv->setGridSize(mySize); - * @endcode - * - * will not call to the expected setGridSize method. Instead do something like this: - * - * @code - * QListView *lv; - * ... - * KCategorizedView *cv = qobject_cast(lv); - * if (cv) { - * cv->setGridSizeOwn(mySize); - * } else { - * lv->setGridSize(mySize); - * } - * @endcode - * - * @note this method will call to QListView::setGridSize among other operations. - * - * @since 4.4 - */ - void setGridSizeOwn ( const QSize &size ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual QRect visualRect ( const QModelIndex &index ) const; - - /** - * Returns the current category drawer. - */ - KCategoryDrawer *categoryDrawer() const; - - /** - * The category drawer that will be used for drawing categories. - */ - void setCategoryDrawer ( KCategoryDrawer *categoryDrawer ); - - /** - * @return Category spacing. The spacing between categories. - * - * @since 4.4 - */ - int categorySpacing() const; - - /** - * Stablishes the category spacing. This is the spacing between categories. - * - * @since 4.4 - */ - void setCategorySpacing ( int categorySpacing ); - - /** - * @return Whether blocks should be drawn with alternating colors. - * - * @since 4.4 - */ - bool alternatingBlockColors() const; - - /** - * Sets whether blocks should be drawn with alternating colors. - * - * @since 4.4 - */ - void setAlternatingBlockColors ( bool enable ); - - /** - * @return Whether blocks can be collapsed or not. - * - * @since 4.4 - */ - bool collapsibleBlocks() const; - - /** - * Sets whether blocks can be collapsed or not. - * - * @since 4.4 - */ - void setCollapsibleBlocks ( bool enable ); - - /** - * @return Block of indexes that are into @p category. - * - * @since 4.5 - */ - QModelIndexList block ( const QString &category ); - - /** - * @return Block of indexes that are represented by @p representative. - * - * @since 4.5 - */ - QModelIndexList block ( const QModelIndex &representative ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual QModelIndex indexAt ( const QPoint &point ) const; - - /** - * Reimplemented from QAbstractItemView. - */ - 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. - */ - virtual void paintEvent ( QPaintEvent *event ); - - /** - * Reimplemented from QWidget. - */ - virtual void resizeEvent ( QResizeEvent *event ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void setSelection ( const QRect &rect, - QItemSelectionModel::SelectionFlags flags ); - - /** - * Reimplemented from QWidget. - */ - virtual void mouseMoveEvent ( QMouseEvent *event ); - - /** - * Reimplemented from QWidget. - */ - virtual void mousePressEvent ( QMouseEvent *event ); - - /** - * Reimplemented from QWidget. - */ - virtual void mouseReleaseEvent ( QMouseEvent *event ); - - /** - * Reimplemented from QWidget. - */ - virtual void leaveEvent ( QEvent *event ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void startDrag ( Qt::DropActions supportedActions ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dragMoveEvent ( QDragMoveEvent *event ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dragEnterEvent ( QDragEnterEvent *event ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dragLeaveEvent ( QDragLeaveEvent *event ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dropEvent ( QDropEvent *event ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual QModelIndex moveCursor ( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void rowsAboutToBeRemoved ( const QModelIndex &parent, - int start, - int end ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void updateGeometries(); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void currentChanged ( const QModelIndex ¤t, - const QModelIndex &previous ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void dataChanged ( const QModelIndex &topLeft, - const QModelIndex &bottomRight ); - - /** - * Reimplemented from QAbstractItemView. - */ - virtual void rowsInserted ( const QModelIndex &parent, - int start, - int end ); - -protected Q_SLOTS: - /** - * @internal - * Reposition items as needed. - */ - virtual void slotLayoutChanged(); - virtual void slotCollapseOrExpandClicked ( QModelIndex ); - -private: - class Private; - Private *const d; -}; - -#endif // KCATEGORIZEDVIEW_H diff --git a/depends/groupview/include/categorydrawer.h b/depends/groupview/include/categorydrawer.h deleted file mode 100644 index f37422ec..00000000 --- a/depends/groupview/include/categorydrawer.h +++ /dev/null @@ -1,179 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KCATEGORYDRAWER_H -#define KCATEGORYDRAWER_H - -#include - -#include -#include - -class QPainter; -class QModelIndex; -class QStyleOption; -class KCategorizedView; - - -/** - * @since 4.5 - */ -class LIBGROUPVIEW_EXPORT KCategoryDrawer - : public QObject -{ - friend class KCategorizedView; - Q_OBJECT - - -public: - KCategoryDrawer ( KCategorizedView *view ); - virtual ~KCategoryDrawer(); - - /** - * @return The view this category drawer is associated with. - */ - KCategorizedView *view() const; - - /** - * This method purpose is to draw a category represented by the given - * @param index with the given @param sortRole sorting role - * - * @note This method will be called one time per category, always with the - * first element in that category - */ - virtual void drawCategory ( const QModelIndex &index, - int sortRole, - const QStyleOption &option, - QPainter *painter ) const; - - /** - * @return The category height for the category representated by index @p index with - * style options @p option. - */ - virtual int categoryHeight ( const QModelIndex &index, const QStyleOption &option ) const; - - //TODO KDE5: make virtual as leftMargin - /** - * @note 0 by default - * - * @since 4.4 - */ - int leftMargin() const; - - /** - * @note call to this method on the KCategoryDrawer constructor to set the left margin - * - * @since 4.4 - */ - void setLeftMargin ( int leftMargin ); - - //TODO KDE5: make virtual as rightMargin - /** - * @note 0 by default - * - * @since 4.4 - */ - int rightMargin() const; - - /** - * @note call to this method on the KCategoryDrawer constructor to set the right margin - * - * @since 4.4 - */ - void setRightMargin ( int rightMargin ); - - KCategoryDrawer &operator= ( const KCategoryDrawer &cd ); -protected: - /** - * Method called when the mouse button has been pressed. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - * - * @warning You explicitly have to determine whether the event has been accepted or not. You - * have to call event->accept() or event->ignore() at all possible case branches in - * your code. - */ - virtual void mouseButtonPressed ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); - - /** - * Method called when the mouse button has been released. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - * - * @warning You explicitly have to determine whether the event has been accepted or not. You - * have to call event->accept() or event->ignore() at all possible case branches in - * your code. - */ - virtual void mouseButtonReleased ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); - - /** - * Method called when the mouse has been moved. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - */ - virtual void mouseMoved ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); - - /** - * Method called when the mouse button has been double clicked. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - * @param event The mouse event. - * - * @warning You explicitly have to determine whether the event has been accepted or not. You - * have to call event->accept() or event->ignore() at all possible case branches in - * your code. - */ - virtual void mouseButtonDoubleClicked ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event ); - - /** - * Method called when the mouse button has left this block. - * - * @param index The representative index of the block of items. - * @param blockRect The rect occupied by the block of items. - */ - virtual void mouseLeft ( const QModelIndex &index, const QRect &blockRect ); - -private: - class Private; - Private *const d; -Q_SIGNALS: - /** - * This signal becomes emitted when collapse or expand has been clicked. - */ - void collapseOrExpandClicked ( const QModelIndex &index ); - - /** - * Emit this signal on your subclass implementation to notify that something happened. Usually - * this will be triggered when you have received an event, and its position matched some "hot spot". - * - * You give this action the integer you want, and having connected this signal to your code, - * the connected slot can perform the needed changes (view, model, selection model, delegate...) - */ - void actionRequested ( int action, const QModelIndex &index ); -}; - -#endif // KCATEGORYDRAWER_H diff --git a/depends/groupview/include/groupview_config.h b/depends/groupview/include/groupview_config.h deleted file mode 100644 index c63acbde..00000000 --- a/depends/groupview/include/groupview_config.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * 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 - -#ifdef LIBGROUPVIEW_STATIC - #define LIBGROUPVIEW_EXPORT -#else - #ifdef LIBGROUPVIEW_LIBRARY - #define LIBGROUPVIEW_EXPORT Q_DECL_EXPORT - #else - #define LIBGROUPVIEW_EXPORT Q_DECL_IMPORT - #endif -#endif diff --git a/depends/groupview/src/categorizedsortfilterproxymodel.cpp b/depends/groupview/src/categorizedsortfilterproxymodel.cpp deleted file mode 100644 index 09da9dd3..00000000 --- a/depends/groupview/src/categorizedsortfilterproxymodel.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López - * Copyright (C) 2007 John Tapsell - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "categorizedsortfilterproxymodel.h" -#include "categorizedsortfilterproxymodel_p.h" - -#include - -#include -#include -#include - -KCategorizedSortFilterProxyModel::KCategorizedSortFilterProxyModel ( QObject *parent ) - : QSortFilterProxyModel ( parent ) - , d ( new Private() ) -{ -} - -KCategorizedSortFilterProxyModel::~KCategorizedSortFilterProxyModel() -{ - delete d; -} - -void KCategorizedSortFilterProxyModel::sort ( int column, Qt::SortOrder order ) -{ - d->sortColumn = column; - d->sortOrder = order; - - QSortFilterProxyModel::sort ( column, order ); -} - -bool KCategorizedSortFilterProxyModel::isCategorizedModel() const -{ - return d->categorizedModel; -} - -void KCategorizedSortFilterProxyModel::setCategorizedModel ( bool categorizedModel ) -{ - if ( categorizedModel == d->categorizedModel ) - { - return; - } - - d->categorizedModel = categorizedModel; - - invalidate(); -} - -int KCategorizedSortFilterProxyModel::sortColumn() const -{ - return d->sortColumn; -} - -Qt::SortOrder KCategorizedSortFilterProxyModel::sortOrder() const -{ - return d->sortOrder; -} - -void KCategorizedSortFilterProxyModel::setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison ) -{ - if ( sortCategoriesByNaturalComparison == d->sortCategoriesByNaturalComparison ) - { - return; - } - - d->sortCategoriesByNaturalComparison = sortCategoriesByNaturalComparison; - - invalidate(); -} - -bool KCategorizedSortFilterProxyModel::sortCategoriesByNaturalComparison() const -{ - return d->sortCategoriesByNaturalComparison; -} - -bool KCategorizedSortFilterProxyModel::lessThan ( const QModelIndex &left, const QModelIndex &right ) const -{ - if ( d->categorizedModel ) - { - int compare = compareCategories ( left, right ); - - if ( compare > 0 ) // left is greater than right - { - return false; - } - else if ( compare < 0 ) // left is less than right - { - return true; - } - } - - return subSortLessThan ( left, right ); -} - -bool KCategorizedSortFilterProxyModel::subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const -{ - return QSortFilterProxyModel::lessThan ( left, right ); -} - -int KCategorizedSortFilterProxyModel::compareCategories ( const QModelIndex &left, const QModelIndex &right ) const -{ - QVariant l = ( left.model() ? left.model()->data ( left, CategorySortRole ) : QVariant() ); - QVariant r = ( right.model() ? right.model()->data ( right, CategorySortRole ) : QVariant() ); - - Q_ASSERT ( l.isValid() ); - Q_ASSERT ( r.isValid() ); - Q_ASSERT ( l.type() == r.type() ); - - if ( l.type() == QVariant::String ) - { - QString lstr = l.toString(); - QString rstr = r.toString(); - - /* - if ( d->sortCategoriesByNaturalComparison ) - { - return KStringHandler::naturalCompare ( lstr, rstr ); - } - else - { - */ - if ( lstr < rstr ) - { - return -1; - } - - if ( lstr > rstr ) - { - return 1; - } - - return 0; - //} - } - - qlonglong lint = l.toLongLong(); - qlonglong rint = r.toLongLong(); - - if ( lint < rint ) - { - return -1; - } - - if ( lint > rint ) - { - return 1; - } - - return 0; -} diff --git a/depends/groupview/src/categorizedsortfilterproxymodel_p.h b/depends/groupview/src/categorizedsortfilterproxymodel_p.h deleted file mode 100644 index d7e7c9a0..00000000 --- a/depends/groupview/src/categorizedsortfilterproxymodel_p.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López - * Copyright (C) 2007 John Tapsell - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_P_H -#define KCATEGORIZEDSORTFILTERPROXYMODEL_P_H - -class KCategorizedSortFilterProxyModel; - -class KCategorizedSortFilterProxyModel::Private -{ -public: - Private() - : sortColumn ( 0 ) - , sortOrder ( Qt::AscendingOrder ) - , categorizedModel ( false ) - , sortCategoriesByNaturalComparison ( true ) - { - } - - ~Private() - { - } - - int sortColumn; - Qt::SortOrder sortOrder; - bool categorizedModel; - bool sortCategoriesByNaturalComparison; -}; - -#endif diff --git a/depends/groupview/src/categorizedview.cpp b/depends/groupview/src/categorizedview.cpp deleted file mode 100644 index 1345205c..00000000 --- a/depends/groupview/src/categorizedview.cpp +++ /dev/null @@ -1,1692 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * IMPLEMENTATION NOTES: - * - * QListView::setRowHidden() and QListView::isRowHidden() are not taken into - * account. This methods should actually not exist. This effect should be handled - * by an hypothetical QSortFilterProxyModel which filters out the desired rows. - * - * In case this needs to be implemented, contact me, but I consider this a faulty - * design. - */ - -#include "categorizedview.h" -#include "categorizedview_p.h" - -#include // trunc on C99 compliant systems -//#include // trunc for not C99 compliant systems - -#include -#include -#include - -#include "categorydrawer.h" -#include "categorizedsortfilterproxymodel.h" - -//BEGIN: Private part - -struct KCategorizedView::Private::Item -{ - Item() - : topLeft ( QPoint() ) - , size ( QSize() ) - { - } - - QPoint topLeft; - QSize size; -}; - -struct KCategorizedView::Private::Block -{ - bool operator!= ( const Block &rhs ) const - { - return firstIndex != rhs.firstIndex; - } - - static bool lessThan ( const Block &left, const Block &right ) - { - Q_ASSERT ( left.firstIndex.isValid() ); - Q_ASSERT ( right.firstIndex.isValid() ); - return left.firstIndex.row() < right.firstIndex.row(); - } - - QPoint topLeft; - int height = -1; - QPersistentModelIndex firstIndex; - // if we have n elements on this block, and we inserted an element at position i. The quarantine - // will start at index (i, column, parent). This means that for all elements j where i <= j <= n, the - // visual rect position of item j will have to be recomputed (cannot use the cached point). The quarantine - // will only affect the current block, since the rest of blocks can be affected only in the way - // that the whole block will have different offset, but items will keep the same relative position - // in terms of their parent blocks. - QPersistentModelIndex quarantineStart; - QList items; - - // this affects the whole block, not items separately. items contain the topLeft point relative - // to the block. Because of insertions or removals a whole block can be moved, so the whole block - // will enter in quarantine, what is faster than moving all items in absolute terms. - bool outOfQuarantine = false; - - // should we alternate its color ? is just a hint, could not be used - bool alternate = false; - bool collapsed = false; -}; - -KCategorizedView::Private::Private ( KCategorizedView *q ) - : q ( q ) - , hoveredBlock ( new Block() ) -{ -} - -KCategorizedView::Private::~Private() -{ - delete hoveredBlock; -} - -bool KCategorizedView::Private::isCategorized() const -{ - return proxyModel && categoryDrawer && proxyModel->isCategorizedModel(); -} - -QStyleOptionViewItemV4 KCategorizedView::Private::blockRect ( const QModelIndex &representative ) -{ - QStyleOptionViewItemV4 option ( q->viewOptions() ); - const int height = categoryDrawer->categoryHeight ( representative, option ); - const QString categoryDisplay = representative.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); - QPoint pos = blockPosition ( categoryDisplay ); - pos.ry() -= height; - option.rect.setTopLeft ( pos ); - option.rect.setWidth ( viewportWidth() + categoryDrawer->leftMargin() + categoryDrawer->rightMargin() ); - option.rect.setHeight ( height + blockHeight ( categoryDisplay ) ); - option.rect = mapToViewport ( option.rect ); - - return option; -} - -QPair KCategorizedView::Private::intersectingIndexesWithRect ( const QRect &_rect ) const -{ - const int rowCount = proxyModel->rowCount(); - - const QRect rect = _rect.normalized(); - - // binary search to find out the top border - int bottom = 0; - int top = rowCount - 1; - while ( bottom <= top ) - { - const int middle = ( bottom + top ) / 2; - const QModelIndex index = proxyModel->index ( middle, q->modelColumn(), q->rootIndex() ); - QRect itemRect = q->visualRect ( index ); - const int verticalOff = q->verticalOffset(); - const int horizontalOff = q->horizontalOffset(); - itemRect.topLeft().ry() += verticalOff; - itemRect.topLeft().rx() += horizontalOff; - itemRect.bottomRight().ry() += verticalOff; - itemRect.bottomRight().rx() += horizontalOff; - if ( itemRect.bottomRight().y() <= rect.topLeft().y() ) - { - bottom = middle + 1; - } - else - { - top = middle - 1; - } - } - - const QModelIndex bottomIndex = proxyModel->index ( bottom, q->modelColumn(), q->rootIndex() ); - - // binary search to find out the bottom border - bottom = 0; - top = rowCount - 1; - while ( bottom <= top ) - { - const int middle = ( bottom + top ) / 2; - const QModelIndex index = proxyModel->index ( middle, q->modelColumn(), q->rootIndex() ); - QRect itemRect = q->visualRect ( index ); - const int verticalOff = q->verticalOffset(); - const int horizontalOff = q->horizontalOffset(); - itemRect.topLeft().ry() += verticalOff; - itemRect.topLeft().rx() += horizontalOff; - itemRect.bottomRight().ry() += verticalOff; - itemRect.bottomRight().rx() += horizontalOff; - if ( itemRect.topLeft().y() <= rect.bottomRight().y() ) - { - bottom = middle + 1; - } - else - { - top = middle - 1; - } - } - - const QModelIndex topIndex = proxyModel->index ( top, q->modelColumn(), q->rootIndex() ); - - return qMakePair ( bottomIndex, topIndex ); -} - -QPoint KCategorizedView::Private::blockPosition ( const QString &category ) -{ - Block &block = blocks[category]; - - if ( block.outOfQuarantine && !block.topLeft.isNull() ) - { - return block.topLeft; - } - - QPoint res ( categorySpacing, 0 ); - - const QModelIndex index = block.firstIndex; - - for ( QHash::Iterator it = blocks.begin(); it != blocks.end(); ++it ) - { - Block &block = *it; - const QModelIndex categoryIndex = block.firstIndex; - if ( index.row() < categoryIndex.row() ) - { - continue; - } - res.ry() += categoryDrawer->categoryHeight ( categoryIndex, q->viewOptions() ) + categorySpacing; - if ( index.row() == categoryIndex.row() ) - { - continue; - } - res.ry() += blockHeight ( it.key() ); - } - - block.outOfQuarantine = true; - block.topLeft = res; - - return res; -} - -int KCategorizedView::Private::blockHeight ( const QString &category ) -{ - Block &block = blocks[category]; - - if ( block.collapsed ) - { - return 0; - } - - if ( block.height > -1 ) - { - return block.height; - } - - const QModelIndex firstIndex = block.firstIndex; - const QModelIndex lastIndex = proxyModel->index ( firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex() ); - const QRect topLeft = q->visualRect ( firstIndex ); - QRect bottomRight = q->visualRect ( lastIndex ); - - if ( hasGrid() ) - { - bottomRight.setHeight ( qMax ( bottomRight.height(), q->gridSize().height() ) ); - } - else - { - if ( !q->uniformItemSizes() ) - { - bottomRight.setHeight ( highestElementInLastRow ( block ) + q->spacing() * 2 ); - } - } - - const int height = bottomRight.bottomRight().y() - topLeft.topLeft().y() + 1; - block.height = height; - - return height; -} - -int KCategorizedView::Private::viewportWidth() const -{ - return q->viewport()->width() - categorySpacing * 2 - categoryDrawer->leftMargin() - categoryDrawer->rightMargin(); -} - -void KCategorizedView::Private::regenerateAllElements() -{ - for ( QHash::Iterator it = blocks.begin(); it != blocks.end(); ++it ) - { - Block &block = *it; - block.outOfQuarantine = false; - block.quarantineStart = block.firstIndex; - block.height = -1; - } -} - -void KCategorizedView::Private::rowsInserted ( const QModelIndex &parent, int start, int end ) -{ - if ( !isCategorized() ) - { - return; - } - - for ( int i = start; i <= end; ++i ) - { - const QModelIndex index = proxyModel->index ( i, q->modelColumn(), parent ); - - Q_ASSERT ( index.isValid() ); - - const QString category = categoryForIndex ( index ); - - Block &block = blocks[category]; - - //BEGIN: update firstIndex - // save as firstIndex in block if - // - it forced the category creation (first element on this category) - // - it is before the first row on that category - const QModelIndex firstIndex = block.firstIndex; - if ( !firstIndex.isValid() || index.row() < firstIndex.row() ) - { - block.firstIndex = index; - } - //END: update firstIndex - - Q_ASSERT ( block.firstIndex.isValid() ); - - const int firstIndexRow = block.firstIndex.row(); - - block.items.insert ( index.row() - firstIndexRow, Private::Item() ); - block.height = -1; - - q->visualRect ( index ); - q->viewport()->update(); - } - - //BEGIN: update the items that are in quarantine in affected categories - { - const QModelIndex lastIndex = proxyModel->index ( end, q->modelColumn(), parent ); - const QString category = categoryForIndex ( lastIndex ); - Private::Block &block = blocks[category]; - block.quarantineStart = block.firstIndex; - } - //END: update the items that are in quarantine in affected categories - - //BEGIN: mark as in quarantine those categories that are under the affected ones - { - const QModelIndex firstIndex = proxyModel->index ( start, q->modelColumn(), parent ); - const QString category = categoryForIndex ( firstIndex ); - const QModelIndex firstAffectedCategory = blocks[category].firstIndex; - //BEGIN: order for marking as alternate those blocks that are alternate - QList blockList = blocks.values(); - qSort ( blockList.begin(), blockList.end(), Block::lessThan ); - QList firstIndexesRows; - foreach ( const Block &block, blockList ) - { - firstIndexesRows << block.firstIndex.row(); - } - //END: order for marking as alternate those blocks that are alternate - for ( QHash::Iterator it = blocks.begin(); it != blocks.end(); ++it ) - { - Private::Block &block = *it; - if ( block.firstIndex.row() > firstAffectedCategory.row() ) - { - block.outOfQuarantine = false; - block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; - } - else if ( block.firstIndex.row() == firstAffectedCategory.row() ) - { - block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; - } - } - } - //END: mark as in quarantine those categories that are under the affected ones -} - -QRect KCategorizedView::Private::mapToViewport ( const QRect &rect ) const -{ - const int dx = -q->horizontalOffset(); - const int dy = -q->verticalOffset(); - return rect.adjusted ( dx, dy, dx, dy ); -} - -QRect KCategorizedView::Private::mapFromViewport ( const QRect &rect ) const -{ - const int dx = q->horizontalOffset(); - const int dy = q->verticalOffset(); - return rect.adjusted ( dx, dy, dx, dy ); -} - -int KCategorizedView::Private::highestElementInLastRow ( const Block &block ) const -{ - //Find the highest element in the last row - const QModelIndex lastIndex = proxyModel->index ( block.firstIndex.row() + block.items.count() - 1, q->modelColumn(), q->rootIndex() ); - const QRect prevRect = q->visualRect ( lastIndex ); - int res = prevRect.height(); - QModelIndex prevIndex = proxyModel->index ( lastIndex.row() - 1, q->modelColumn(), q->rootIndex() ); - if ( !prevIndex.isValid() ) - { - return res; - } - Q_FOREVER - { - const QRect tempRect = q->visualRect ( prevIndex ); - if ( tempRect.topLeft().y() < prevRect.topLeft().y() ) - { - break; - } - res = qMax ( res, tempRect.height() ); - if ( prevIndex == block.firstIndex ) - { - break; - } - prevIndex = proxyModel->index ( prevIndex.row() - 1, q->modelColumn(), q->rootIndex() ); - } - - return res; -} - -bool KCategorizedView::Private::hasGrid() const -{ - const QSize gridSize = q->gridSize(); - return gridSize.isValid() && !gridSize.isNull(); -} - -QString KCategorizedView::Private::categoryForIndex ( const QModelIndex &index ) const -{ - const QModelIndex categoryIndex = index.model()->index ( index.row(), proxyModel->sortColumn(), index.parent() ); - return categoryIndex.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); -} - -void KCategorizedView::Private::leftToRightVisualRect ( const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos ) const -{ - const int firstIndexRow = block.firstIndex.row(); - - if ( hasGrid() ) - { - const int relativeRow = index.row() - firstIndexRow; - const int maxItemsPerRow = qMax ( viewportWidth() / q->gridSize().width(), 1 ); - if ( q->layoutDirection() == Qt::LeftToRight ) - { - item.topLeft.rx() = ( relativeRow % maxItemsPerRow ) * q->gridSize().width() + blockPos.x() + categoryDrawer->leftMargin(); - } - else - { - item.topLeft.rx() = viewportWidth() - ( ( relativeRow % maxItemsPerRow ) + 1 ) * q->gridSize().width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = ( relativeRow / maxItemsPerRow ) * q->gridSize().height(); - } - else - { - if ( q->uniformItemSizes() /*|| q->uniformItemWidths()*/ ) - { - const int relativeRow = index.row() - firstIndexRow; - const QSize itemSize = q->sizeHintForIndex ( index ); - //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(); - } - else - { - item.topLeft.rx() = viewportWidth() - ( relativeRow % maxItemsPerRow ) * itemSize.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = ( relativeRow / maxItemsPerRow ) * itemSize.height(); - } - else - { - const QSize currSize = q->sizeHintForIndex ( index ); - if ( index != block.firstIndex ) - { - const int viewportW = viewportWidth() - q->spacing(); - QModelIndex prevIndex = proxyModel->index ( index.row() - 1, q->modelColumn(), q->rootIndex() ); - QRect prevRect = q->visualRect ( prevIndex ); - prevRect = mapFromViewport ( prevRect ); - if ( ( prevRect.bottomRight().x() + 1 ) + currSize.width() - blockPos.x() + q->spacing() > viewportW ) - { - // we have to check the whole previous row, and see which one was the - // highest. - Q_FOREVER - { - prevIndex = proxyModel->index ( prevIndex.row() - 1, q->modelColumn(), q->rootIndex() ); - QRect tempRect = q->visualRect ( prevIndex ); - tempRect = mapFromViewport ( tempRect ); - if ( tempRect.topLeft().y() < prevRect.topLeft().y() ) - { - break; - } - if ( tempRect.bottomRight().y() > prevRect.bottomRight().y() ) - { - prevRect = tempRect; - } - if ( prevIndex == block.firstIndex ) - { - break; - } - } - if ( q->layoutDirection() == Qt::LeftToRight ) - { - item.topLeft.rx() = categoryDrawer->leftMargin() + blockPos.x() + q->spacing(); - } - else - { - item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = ( prevRect.bottomRight().y() + 1 ) + q->spacing() - blockPos.y(); - } - else - { - if ( q->layoutDirection() == Qt::LeftToRight ) - { - item.topLeft.rx() = ( prevRect.bottomRight().x() + 1 ) + q->spacing(); - } - else - { - item.topLeft.rx() = ( prevRect.bottomLeft().x() - 1 ) - q->spacing() - item.size.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = prevRect.topLeft().y() - blockPos.y(); - } - } - else - { - if ( q->layoutDirection() == Qt::LeftToRight ) - { - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); - } - else - { - item.topLeft.rx() = viewportWidth() - currSize.width() + categoryDrawer->leftMargin() + categorySpacing; - } - item.topLeft.ry() = q->spacing(); - } - } - } - item.size = q->sizeHintForIndex ( index ); -} - -void KCategorizedView::Private::topToBottomVisualRect ( const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos ) const -{ - const int firstIndexRow = block.firstIndex.row(); - - if ( hasGrid() ) - { - const int relativeRow = index.row() - firstIndexRow; - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin(); - item.topLeft.ry() = relativeRow * q->gridSize().height(); - } - else - { - if ( q->uniformItemSizes() ) - { - const int relativeRow = index.row() - firstIndexRow; - const QSize itemSize = q->sizeHintForIndex ( index ); - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin(); - item.topLeft.ry() = relativeRow * itemSize.height(); - } - else - { - if ( index != block.firstIndex ) - { - QModelIndex prevIndex = proxyModel->index ( index.row() - 1, q->modelColumn(), q->rootIndex() ); - QRect prevRect = q->visualRect ( prevIndex ); - prevRect = mapFromViewport ( prevRect ); - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); - item.topLeft.ry() = ( prevRect.bottomRight().y() + 1 ) + q->spacing() - blockPos.y(); - } - else - { - item.topLeft.rx() = blockPos.x() + categoryDrawer->leftMargin() + q->spacing(); - item.topLeft.ry() = q->spacing(); - } - } - } - item.size = q->sizeHintForIndex ( index ); - item.size.setWidth ( viewportWidth() ); -} - -void KCategorizedView::Private::_k_slotCollapseOrExpandClicked ( QModelIndex ) -{ -} - -//END: Private part - -//BEGIN: Public part - -KCategorizedView::KCategorizedView ( QWidget *parent ) - : QListView ( parent ) - , d ( new Private ( this ) ) -{ -} - -KCategorizedView::~KCategorizedView() -{ - delete d; -} - -void KCategorizedView::setModel ( QAbstractItemModel *model ) -{ - if ( d->proxyModel == model ) - { - return; - } - - d->blocks.clear(); - - if ( d->proxyModel ) - { - disconnect ( d->proxyModel, SIGNAL ( layoutChanged() ), this, SLOT ( slotLayoutChanged() ) ); - } - - d->proxyModel = dynamic_cast ( model ); - - if ( d->proxyModel ) - { - connect ( d->proxyModel, SIGNAL ( layoutChanged() ), this, SLOT ( slotLayoutChanged() ) ); - } - - QListView::setModel ( model ); - - // if the model already had information inserted, update our data structures to it - if ( model->rowCount() ) - { - slotLayoutChanged(); - } -} - - -void KCategorizedView::setUniformItemWidths(bool enable) -{ - d->constantItemWidth = enable; -} - - -bool KCategorizedView::uniformItemWidths() const -{ - return d->constantItemWidth; -} - -void KCategorizedView::setGridSize ( const QSize &size ) -{ - setGridSizeOwn ( size ); -} - -void KCategorizedView::setGridSizeOwn ( const QSize &size ) -{ - d->regenerateAllElements(); - QListView::setGridSize ( size ); -} - -QRect KCategorizedView::visualRect ( const QModelIndex &index ) const -{ - if ( !d->isCategorized() ) - { - return QListView::visualRect ( index ); - } - - if ( !index.isValid() ) - { - return QRect(); - } - - const QString category = d->categoryForIndex ( index ); - - if ( !d->blocks.contains ( category ) ) - { - return QRect(); - } - - Private::Block &block = d->blocks[category]; - const int firstIndexRow = block.firstIndex.row(); - - Q_ASSERT ( block.firstIndex.isValid() ); - - if ( index.row() - firstIndexRow < 0 || index.row() - firstIndexRow >= block.items.count() ) - { - return QRect(); - } - - const QPoint blockPos = d->blockPosition ( category ); - - Private::Item &ritem = block.items[index.row() - firstIndexRow]; - - if ( ritem.topLeft.isNull() || ( block.quarantineStart.isValid() && - index.row() >= block.quarantineStart.row() ) ) - { - if ( flow() == LeftToRight ) - { - d->leftToRightVisualRect ( index, ritem, block, blockPos ); - } - else - { - d->topToBottomVisualRect ( index, ritem, block, blockPos ); - } - - //BEGIN: update the quarantine start - const bool wasLastIndex = ( index.row() == ( block.firstIndex.row() + block.items.count() - 1 ) ); - if ( index.row() == block.quarantineStart.row() ) - { - if ( wasLastIndex ) - { - block.quarantineStart = QModelIndex(); - } - else - { - const QModelIndex nextIndex = d->proxyModel->index ( index.row() + 1, modelColumn(), rootIndex() ); - block.quarantineStart = nextIndex; - } - } - //END: update the quarantine start - } - - // we get now the absolute position through the relative position of the parent block. do not - // save this on ritem, since this would override the item relative position in block terms. - Private::Item item ( ritem ); - item.topLeft.ry() += blockPos.y(); - - const QSize sizeHint = item.size; - - if ( d->hasGrid() ) - { - const QSize sizeGrid = gridSize(); - const QSize resultingSize = sizeHint.boundedTo ( sizeGrid ); - QRect res ( item.topLeft.x() + ( ( sizeGrid.width() - resultingSize.width() ) / 2 ), - item.topLeft.y(), resultingSize.width(), resultingSize.height() ); - if ( block.collapsed ) - { - // we can still do binary search, while we "hide" items. We move those items in collapsed - // blocks to the left and set a 0 height. - res.setLeft ( -resultingSize.width() ); - res.setHeight ( 0 ); - } - return d->mapToViewport ( res ); - } - - QRect res ( item.topLeft.x(), item.topLeft.y(), sizeHint.width(), sizeHint.height() ); - if ( block.collapsed ) - { - // we can still do binary search, while we "hide" items. We move those items in collapsed - // blocks to the left and set a 0 height. - res.setLeft ( -sizeHint.width() ); - res.setHeight ( 0 ); - } - return d->mapToViewport ( res ); -} - -KCategoryDrawer *KCategorizedView::categoryDrawer() const -{ - return d->categoryDrawer; -} - -void KCategorizedView::setCategoryDrawer ( KCategoryDrawer *categoryDrawer ) -{ - disconnect ( d->categoryDrawer, SIGNAL ( collapseOrExpandClicked ( QModelIndex ) ), - this, SLOT ( slotCollapseOrExpandClicked ( QModelIndex ) ) ); - d->categoryDrawer = categoryDrawer; - - connect ( d->categoryDrawer, SIGNAL ( collapseOrExpandClicked ( QModelIndex ) ), - this, SLOT ( slotCollapseOrExpandClicked ( QModelIndex ) ) ); -} - -int KCategorizedView::categorySpacing() const -{ - return d->categorySpacing; -} - -void KCategorizedView::setCategorySpacing ( int categorySpacing ) -{ - if ( d->categorySpacing == categorySpacing ) - { - return; - } - - d->categorySpacing = categorySpacing; - - for ( QHash::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it ) - { - Private::Block &block = *it; - block.outOfQuarantine = false; - } -} - -bool KCategorizedView::alternatingBlockColors() const -{ - return d->alternatingBlockColors; -} - -void KCategorizedView::setAlternatingBlockColors ( bool enable ) -{ - d->alternatingBlockColors = enable; -} - -bool KCategorizedView::collapsibleBlocks() const -{ - return d->collapsibleBlocks; -} - -void KCategorizedView::setCollapsibleBlocks ( bool enable ) -{ - d->collapsibleBlocks = enable; -} - -QModelIndexList KCategorizedView::block ( const QString &category ) -{ - QModelIndexList res; - const Private::Block &block = d->blocks[category]; - if ( block.height == -1 ) - { - return res; - } - QModelIndex current = block.firstIndex; - const int first = current.row(); - for ( int i = 1; i <= block.items.count(); ++i ) - { - if ( current.isValid() ) - { - res << current; - } - current = d->proxyModel->index ( first + i, modelColumn(), rootIndex() ); - } - return res; -} - -QModelIndexList KCategorizedView::block ( const QModelIndex &representative ) -{ - return block ( representative.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString() ); -} - -QModelIndex KCategorizedView::indexAt ( const QPoint &point ) const -{ - if ( !d->isCategorized() ) - { - return QListView::indexAt ( point ); - } - - const int rowCount = d->proxyModel->rowCount(); - if ( !rowCount ) - { - return QModelIndex(); - } - - // Binary search that will try to spot if there is an index under point - int bottom = 0; - int top = rowCount - 1; - while ( bottom <= top ) - { - const int middle = ( bottom + top ) / 2; - const QModelIndex index = d->proxyModel->index ( middle, modelColumn(), rootIndex() ); - QRect rect = visualRect ( index ); - const int verticalOff = verticalOffset(); - int horizontalOff = horizontalOffset(); - if ( layoutDirection() == Qt::RightToLeft ) - { - horizontalOff *= -1; - } - rect.topLeft().ry() += verticalOff; - rect.topLeft().rx() += horizontalOff; - rect.bottomRight().ry() += verticalOff; - rect.bottomRight().rx() += horizontalOff; - if ( rect.contains ( point ) ) - { - if ( index.model()->flags ( index ) & Qt::ItemIsEnabled ) - { - return index; - } - return QModelIndex(); - } - bool directionCondition; - if ( layoutDirection() == Qt::LeftToRight ) - { - directionCondition = point.x() > rect.bottomRight().x(); - } - else - { - directionCondition = point.x() < rect.bottomLeft().x(); - } - if ( point.y() > rect.bottomRight().y() || - ( point.y() > rect.topLeft().y() && point.y() < rect.bottomRight().y() && directionCondition ) ) - { - bottom = middle + 1; - } - else - { - top = middle - 1; - } - } - return QModelIndex(); -} - -void KCategorizedView::reset() -{ - d->blocks.clear(); - QListView::reset(); -} - -void KCategorizedView::paintEvent ( QPaintEvent *event ) -{ - if ( !d->isCategorized() ) - { - QListView::paintEvent ( event ); - return; - } - - const QPair intersecting = d->intersectingIndexesWithRect ( viewport()->rect().intersected ( event->rect() ) ); - - QPainter p ( viewport() ); - p.save(); - - Q_ASSERT ( selectionModel()->model() == d->proxyModel ); - - //BEGIN: draw categories - QHash::ConstIterator it ( d->blocks.constBegin() ); - while ( it != d->blocks.constEnd() ) - { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - QStyleOptionViewItemV4 option ( viewOptions() ); - option.features |= d->alternatingBlockColors && block.alternate ? QStyleOptionViewItemV4::Alternate - : QStyleOptionViewItemV4::None; - option.state |= !d->collapsibleBlocks || !block.collapsed ? QStyle::State_Open - : QStyle::State_None; - const int height = d->categoryDrawer->categoryHeight ( categoryIndex, option ); - QPoint pos = d->blockPosition ( it.key() ); - pos.ry() -= height; - option.rect.setTopLeft ( pos ); - option.rect.setWidth ( d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin() ); - option.rect.setHeight ( height + d->blockHeight ( it.key() ) ); - option.rect = d->mapToViewport ( option.rect ); - if ( !option.rect.intersects ( viewport()->rect() ) ) - { - ++it; - continue; - } - d->categoryDrawer->drawCategory ( categoryIndex, d->proxyModel->sortRole(), option, &p ); - ++it; - } - //END: draw categories - - if ( intersecting.first.isValid() && intersecting.second.isValid() ) - { - //BEGIN: draw items - int i = intersecting.first.row(); - int indexToCheckIfBlockCollapsed = i; - QModelIndex categoryIndex; - QString category; - Private::Block *block = 0; - while ( i <= intersecting.second.row() ) - { - //BEGIN: first check if the block is collapsed. if so, we have to skip the item painting - if ( i == indexToCheckIfBlockCollapsed ) - { - categoryIndex = d->proxyModel->index ( i, d->proxyModel->sortColumn(), rootIndex() ); - category = categoryIndex.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); - block = &d->blocks[category]; - indexToCheckIfBlockCollapsed = block->firstIndex.row() + block->items.count(); - if ( block->collapsed ) - { - i = indexToCheckIfBlockCollapsed; - continue; - } - } - //END: first check if the block is collapsed. if so, we have to skip the item painting - - Q_ASSERT ( block ); - - const bool alternateItem = ( i - block->firstIndex.row() ) % 2; - - const QModelIndex index = d->proxyModel->index ( i, modelColumn(), rootIndex() ); - const Qt::ItemFlags flags = d->proxyModel->flags ( index ); - QStyleOptionViewItemV4 option ( viewOptions() ); - option.rect = visualRect ( index ); - option.widget = this; - option.features |= wordWrap() ? QStyleOptionViewItemV2::WrapText - : QStyleOptionViewItemV2::None; - option.features |= alternatingRowColors() && alternateItem ? QStyleOptionViewItemV4::Alternate - : QStyleOptionViewItemV4::None; - if ( flags & Qt::ItemIsSelectable ) - { - option.state |= selectionModel()->isSelected ( index ) ? QStyle::State_Selected - : QStyle::State_None; - } - else - { - option.state &= ~QStyle::State_Selected; - } - option.state |= ( index == currentIndex() ) ? QStyle::State_HasFocus - : QStyle::State_None; - if ( ! ( flags & Qt::ItemIsEnabled ) ) - { - option.state &= ~QStyle::State_Enabled; - } - else - { - option.state |= ( index == d->hoveredIndex ) ? QStyle::State_MouseOver - : QStyle::State_None; - } - - itemDelegate ( index )->paint ( &p, option, index ); - ++i; - } - //END: draw items - } - - //BEGIN: draw selection rect - if ( isSelectionRectVisible() && d->rubberBandRect.isValid() ) - { - QStyleOptionRubberBand opt; - opt.initFrom ( this ); - opt.shape = QRubberBand::Rectangle; - opt.opaque = false; - opt.rect = d->mapToViewport ( d->rubberBandRect ).intersected ( viewport()->rect().adjusted ( -16, -16, 16, 16 ) ); - p.save(); - style()->drawControl ( QStyle::CE_RubberBand, &opt, &p ); - p.restore(); - } - //END: draw selection rect - - p.restore(); -} - -void KCategorizedView::resizeEvent ( QResizeEvent *event ) -{ - d->regenerateAllElements(); - QListView::resizeEvent ( event ); -} - -void KCategorizedView::setSelection ( const QRect &rect, - QItemSelectionModel::SelectionFlags flags ) -{ - if ( !d->isCategorized() ) - { - QListView::setSelection ( rect, flags ); - return; - } - - if ( rect.topLeft() == rect.bottomRight() ) - { - const QModelIndex index = indexAt ( rect.topLeft() ); - selectionModel()->select ( index, flags ); - return; - } - - const QPair intersecting = d->intersectingIndexesWithRect ( rect ); - - QItemSelection selection; - - //TODO: think of a faster implementation - QModelIndex firstIndex; - QModelIndex lastIndex; - for ( int i = intersecting.first.row(); i <= intersecting.second.row(); ++i ) - { - const QModelIndex index = d->proxyModel->index ( i, modelColumn(), rootIndex() ); - const bool visualRectIntersects = visualRect ( index ).intersects ( rect ); - if ( firstIndex.isValid() ) - { - if ( visualRectIntersects ) - { - lastIndex = index; - } - else - { - selection << QItemSelectionRange ( firstIndex, lastIndex ); - firstIndex = QModelIndex(); - } - } - else if ( visualRectIntersects ) - { - firstIndex = index; - lastIndex = index; - } - } - - if ( firstIndex.isValid() ) - { - selection << QItemSelectionRange ( firstIndex, lastIndex ); - } - - selectionModel()->select ( selection, flags ); -} - -void KCategorizedView::mouseMoveEvent ( QMouseEvent *event ) -{ - QListView::mouseMoveEvent ( event ); - d->hoveredIndex = indexAt ( event->pos() ); - const SelectionMode itemViewSelectionMode = selectionMode(); - if ( state() == DragSelectingState && isSelectionRectVisible() && itemViewSelectionMode != SingleSelection - && itemViewSelectionMode != NoSelection ) - { - QRect rect ( d->pressedPosition, event->pos() + QPoint ( horizontalOffset(), verticalOffset() ) ); - rect = rect.normalized(); - update ( rect.united ( d->rubberBandRect ) ); - d->rubberBandRect = rect; - } - QHash::ConstIterator it ( d->blocks.constBegin() ); - while ( it != d->blocks.constEnd() ) - { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - QStyleOptionViewItemV4 option ( viewOptions() ); - const int height = d->categoryDrawer->categoryHeight ( categoryIndex, option ); - QPoint pos = d->blockPosition ( it.key() ); - pos.ry() -= height; - option.rect.setTopLeft ( pos ); - option.rect.setWidth ( d->viewportWidth() + d->categoryDrawer->leftMargin() + d->categoryDrawer->rightMargin() ); - option.rect.setHeight ( height + d->blockHeight ( it.key() ) ); - option.rect = d->mapToViewport ( option.rect ); - const QPoint mousePos = viewport()->mapFromGlobal ( QCursor::pos() ); - if ( option.rect.contains ( mousePos ) ) - { - if ( d->categoryDrawer && d->hoveredBlock->height != -1 && *d->hoveredBlock != block ) - { - const QModelIndex categoryIndex = d->proxyModel->index ( d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); - d->categoryDrawer->mouseLeft ( categoryIndex, option.rect ); - *d->hoveredBlock = block; - d->hoveredCategory = it.key(); - viewport()->update ( option.rect ); - } - else if ( d->hoveredBlock->height == -1 ) - { - *d->hoveredBlock = block; - d->hoveredCategory = it.key(); - } - else if ( d->categoryDrawer ) - { - d->categoryDrawer->mouseMoved ( categoryIndex, option.rect, event ); - } - viewport()->update ( option.rect ); - return; - } - ++it; - } - if ( d->categoryDrawer && d->hoveredBlock->height != -1 ) - { - const QModelIndex categoryIndex = d->proxyModel->index ( d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); - d->categoryDrawer->mouseLeft ( categoryIndex, option.rect ); - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - viewport()->update ( option.rect ); - } -} - -void KCategorizedView::mousePressEvent ( QMouseEvent *event ) -{ - if ( event->button() == Qt::LeftButton ) - { - d->pressedPosition = event->pos(); - d->pressedPosition.rx() += horizontalOffset(); - d->pressedPosition.ry() += verticalOffset(); - } - if ( !d->categoryDrawer ) - { - QListView::mousePressEvent ( event ); - return; - } - QHash::ConstIterator it ( d->blocks.constBegin() ); - while ( it != d->blocks.constEnd() ) - { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); - const QPoint mousePos = viewport()->mapFromGlobal ( QCursor::pos() ); - if ( option.rect.contains ( mousePos ) ) - { - if ( d->categoryDrawer ) - { - d->categoryDrawer->mouseButtonPressed ( categoryIndex, option.rect, event ); - } - viewport()->update ( option.rect ); - if ( !event->isAccepted() ) - { - QListView::mousePressEvent ( event ); - } - return; - } - ++it; - } - QListView::mousePressEvent ( event ); -} - -void KCategorizedView::mouseReleaseEvent ( QMouseEvent *event ) -{ - d->pressedPosition = QPoint(); - d->rubberBandRect = QRect(); - QHash::ConstIterator it ( d->blocks.constBegin() ); - while ( it != d->blocks.constEnd() ) - { - const Private::Block &block = *it; - const QModelIndex categoryIndex = d->proxyModel->index ( block.firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); - const QPoint mousePos = viewport()->mapFromGlobal ( QCursor::pos() ); - if ( option.rect.contains ( mousePos ) ) - { - if ( d->categoryDrawer ) - { - d->categoryDrawer->mouseButtonReleased ( categoryIndex, option.rect, event ); - } - viewport()->update ( option.rect ); - if ( !event->isAccepted() ) - { - QListView::mouseReleaseEvent ( event ); - } - return; - } - ++it; - } - QListView::mouseReleaseEvent ( event ); -} - -void KCategorizedView::leaveEvent ( QEvent *event ) -{ - QListView::leaveEvent ( event ); - if ( d->hoveredIndex.isValid() ) - { - viewport()->update ( visualRect ( d->hoveredIndex ) ); - d->hoveredIndex = QModelIndex(); - } - if ( d->categoryDrawer && d->hoveredBlock->height != -1 ) - { - const QModelIndex categoryIndex = d->proxyModel->index ( d->hoveredBlock->firstIndex.row(), d->proxyModel->sortColumn(), rootIndex() ); - const QStyleOptionViewItemV4 option = d->blockRect ( categoryIndex ); - d->categoryDrawer->mouseLeft ( categoryIndex, option.rect ); - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - viewport()->update ( option.rect ); - } -} - -void KCategorizedView::startDrag ( Qt::DropActions supportedActions ) -{ - QListView::startDrag ( supportedActions ); -} - -void KCategorizedView::dragMoveEvent ( QDragMoveEvent *event ) -{ - QListView::dragMoveEvent ( event ); - d->hoveredIndex = indexAt ( event->pos() ); -} - -void KCategorizedView::dragEnterEvent ( QDragEnterEvent *event ) -{ - QListView::dragEnterEvent ( event ); -} - -void KCategorizedView::dragLeaveEvent ( QDragLeaveEvent *event ) -{ - QListView::dragLeaveEvent ( event ); -} - -void KCategorizedView::dropEvent ( QDropEvent *event ) -{ - QListView::dropEvent ( event ); -} - -//TODO: improve se we take into account collapsed blocks -//TODO: take into account when there is no grid and no uniformItemSizes -QModelIndex KCategorizedView::moveCursor ( CursorAction cursorAction, - Qt::KeyboardModifiers modifiers ) -{ - if ( !d->isCategorized() ) - { - return QListView::moveCursor ( cursorAction, modifiers ); - } - - const QModelIndex current = currentIndex(); - const QRect currentRect = visualRect ( current ); - if ( !current.isValid() ) - { - const int rowCount = d->proxyModel->rowCount ( rootIndex() ); - if ( !rowCount ) - { - return QModelIndex(); - } - return d->proxyModel->index ( 0, modelColumn(), rootIndex() ); - } - - switch ( cursorAction ) - { - case MoveLeft: - { - if ( !current.row() ) - { - return QModelIndex(); - } - const QModelIndex previous = d->proxyModel->index ( current.row() - 1, modelColumn(), rootIndex() ); - const QRect previousRect = visualRect ( previous ); - if ( previousRect.top() == currentRect.top() ) - { - return previous; - } - - return QModelIndex(); - } - case MoveRight: - { - if ( current.row() == d->proxyModel->rowCount() - 1 ) - { - return QModelIndex(); - } - const QModelIndex next = d->proxyModel->index ( current.row() + 1, modelColumn(), rootIndex() ); - const QRect nextRect = visualRect ( next ); - if ( nextRect.top() == currentRect.top() ) - { - return next; - } - - return QModelIndex(); - } - case MoveDown: - { - 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 )]; - //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(); - - if ( canMove ) - { - return d->proxyModel->index ( current.row() + maxItemsPerRow, modelColumn(), rootIndex() ); - } - - const int currentRelativePos = ( current.row() - block.firstIndex.row() ) % maxItemsPerRow; - const QModelIndex nextIndex = d->proxyModel->index ( block.firstIndex.row() + block.items.count(), modelColumn(), rootIndex() ); - - if ( !nextIndex.isValid() ) - { - return QModelIndex(); - } - - const Private::Block &nextBlock = d->blocks[d->categoryForIndex ( nextIndex )]; - - if ( nextBlock.items.count() <= currentRelativePos ) - { - return QModelIndex(); - } - - if ( currentRelativePos < ( block.items.count() % maxItemsPerRow ) ) - { - return d->proxyModel->index ( nextBlock.firstIndex.row() + currentRelativePos, modelColumn(), rootIndex() ); - } - - return QModelIndex(); - } - } - case MoveUp: - { - 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 )]; - //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 ) - { - return d->proxyModel->index ( current.row() - maxItemsPerRow, modelColumn(), rootIndex() ); - } - - const int currentRelativePos = ( current.row() - block.firstIndex.row() ) % maxItemsPerRow; - const QModelIndex prevIndex = d->proxyModel->index ( block.firstIndex.row() - 1, modelColumn(), rootIndex() ); - - if ( !prevIndex.isValid() ) - { - return QModelIndex(); - } - - const Private::Block &prevBlock = d->blocks[d->categoryForIndex ( prevIndex )]; - - if ( prevBlock.items.count() <= currentRelativePos ) - { - return QModelIndex(); - } - - const int remainder = prevBlock.items.count() % maxItemsPerRow; - if ( currentRelativePos < remainder ) - { - return d->proxyModel->index ( prevBlock.firstIndex.row() + prevBlock.items.count() - remainder + currentRelativePos, modelColumn(), rootIndex() ); - } - - return QModelIndex(); - } - } - default: - break; - } - - return QModelIndex(); -} - -void KCategorizedView::rowsAboutToBeRemoved ( const QModelIndex &parent, - int start, - int end ) -{ - if ( !d->isCategorized() ) - { - QListView::rowsAboutToBeRemoved ( parent, start, end ); - return; - } - - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - - if ( end - start + 1 == d->proxyModel->rowCount() ) - { - d->blocks.clear(); - QListView::rowsAboutToBeRemoved ( parent, start, end ); - return; - } - - // Removal feels a bit more complicated than insertion. Basically we can consider there are - // 3 different cases when going to remove items. (*) represents an item, Items between ([) and - // (]) are the ones which are marked for removal. - // - // - 1st case: - // ... * * * * * * [ * * * ... - // - // The items marked for removal are the last part of this category. No need to mark any item - // of this category as in quarantine, because no special offset will be pushed to items at - // the right because of any changes (since the removed items are those on the right most part - // of the category). - // - // - 2nd case: - // ... * * * * * * ] * * * ... - // - // The items marked for removal are the first part of this category. We have to mark as in - // quarantine all items in this category. Absolutely all. All items will have to be moved to - // the left (or moving up, because rows got a different offset). - // - // - 3rd case: - // ... * * [ * * * * ] * * ... - // - // The items marked for removal are in between of this category. We have to mark as in - // quarantine only those items that are at the right of the end of the removal interval, - // (starting on "]"). - // - // It hasn't been explicitly said, but when we remove, we have to mark all blocks that are - // located under the top most affected category as in quarantine (the block itself, as a whole), - // because such a change can force it to have a different offset (note that items themselves - // contain relative positions to the block, so marking the block as in quarantine is enough). - // - // Also note that removal implicitly means that we have to update correctly firstIndex of each - // block, and in general keep updated the internal information of elements. - - QStringList listOfCategoriesMarkedForRemoval; - - QString lastCategory; - int alreadyRemoved = 0; - for ( int i = start; i <= end; ++i ) - { - const QModelIndex index = d->proxyModel->index ( i, modelColumn(), parent ); - - Q_ASSERT ( index.isValid() ); - - const QString category = d->categoryForIndex ( index ); - - if ( lastCategory != category ) - { - lastCategory = category; - alreadyRemoved = 0; - } - - Private::Block &block = d->blocks[category]; - block.items.removeAt ( i - block.firstIndex.row() - alreadyRemoved ); - ++alreadyRemoved; - - if ( !block.items.count() ) - { - listOfCategoriesMarkedForRemoval << category; - } - - block.height = -1; - - viewport()->update(); - } - - //BEGIN: update the items that are in quarantine in affected categories - { - const QModelIndex lastIndex = d->proxyModel->index ( end, modelColumn(), parent ); - const QString category = d->categoryForIndex ( lastIndex ); - Private::Block &block = d->blocks[category]; - if ( block.items.count() && start <= block.firstIndex.row() && end >= block.firstIndex.row() ) - { - block.firstIndex = d->proxyModel->index ( end + 1, modelColumn(), parent ); - } - block.quarantineStart = block.firstIndex; - } - //END: update the items that are in quarantine in affected categories - - Q_FOREACH ( const QString &category, listOfCategoriesMarkedForRemoval ) - { - d->blocks.remove ( category ); - } - - //BEGIN: mark as in quarantine those categories that are under the affected ones - { - //BEGIN: order for marking as alternate those blocks that are alternate - QList blockList = d->blocks.values(); - qSort ( blockList.begin(), blockList.end(), Private::Block::lessThan ); - QList firstIndexesRows; - foreach ( const Private::Block &block, blockList ) - { - firstIndexesRows << block.firstIndex.row(); - } - //END: order for marking as alternate those blocks that are alternate - for ( QHash::Iterator it = d->blocks.begin(); it != d->blocks.end(); ++it ) - { - Private::Block &block = *it; - if ( block.firstIndex.row() > start ) - { - block.outOfQuarantine = false; - block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; - } - else if ( block.firstIndex.row() == start ) - { - block.alternate = firstIndexesRows.indexOf ( block.firstIndex.row() ) % 2; - } - } - } - //END: mark as in quarantine those categories that are under the affected ones - - QListView::rowsAboutToBeRemoved ( parent, start, end ); -} - -void KCategorizedView::updateGeometries() -{ - const int oldVerticalOffset = verticalOffset(); - const Qt::ScrollBarPolicy verticalP = verticalScrollBarPolicy(), horizontalP = horizontalScrollBarPolicy(); - - //BEGIN bugs 213068, 287847 ------------------------------------------------------------ - /* - * QListView::updateGeometries() has it's own opinion on whether the scrollbars should be visible (valid range) or not - * and triggers a (sometimes additionally timered) resize through ::layoutChildren() - * http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/itemviews/qlistview.cpp#line1499 - * (the comment above the main block isn't all accurate, layoutChldren is called regardless of the policy) - * - * As a result QListView and KCategorizedView occasionally started a race on the scrollbar visibility, effectively blocking the UI - * So we prevent QListView from having an own opinion on the scrollbar visibility by - * fixing it before calling the baseclass QListView::updateGeometries() - * - * Since the implicit show/hide by the followin range setting will cause further resizes if the policy is Qt::ScrollBarAsNeeded - * we keep it static until we're done, then restore the original value and ultimately change the scrollbar visibility ourself. - */ - if ( d->isCategorized() ) // important! - otherwise we'd pollute the setting if the view is initially not categorized - { - setVerticalScrollBarPolicy ( ( verticalP == Qt::ScrollBarAlwaysOn || verticalScrollBar()->isVisibleTo ( this ) ) ? - Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff ); - setHorizontalScrollBarPolicy ( ( horizontalP == Qt::ScrollBarAlwaysOn || horizontalScrollBar()->isVisibleTo ( this ) ) ? - Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff ); - } - //END bugs 213068, 287847 -------------------------------------------------------------- - - QListView::updateGeometries(); - - if ( !d->isCategorized() ) - { - return; - } - - const int rowCount = d->proxyModel->rowCount(); - if ( !rowCount ) - { - verticalScrollBar()->setRange ( 0, 0 ); - // unconditional, see function end todo - horizontalScrollBar()->setRange ( 0, 0 ); - return; - } - - const QModelIndex lastIndex = d->proxyModel->index ( rowCount - 1, modelColumn(), rootIndex() ); - Q_ASSERT ( lastIndex.isValid() ); - QRect lastItemRect = visualRect ( lastIndex ); - - if ( d->hasGrid() ) - { - lastItemRect.setSize ( lastItemRect.size().expandedTo ( gridSize() ) ); - } - else - { - if ( uniformItemSizes() ) - { - QSize itemSize = sizeHintForIndex ( lastIndex ); - itemSize.setHeight ( itemSize.height() + spacing() ); - lastItemRect.setSize ( itemSize ); - } - else - { - QSize itemSize = sizeHintForIndex ( lastIndex ); - const QString category = d->categoryForIndex ( lastIndex ); - itemSize.setHeight ( d->highestElementInLastRow ( d->blocks[category] ) + spacing() ); - lastItemRect.setSize ( itemSize ); - } - } - - const int bottomRange = lastItemRect.bottomRight().y() + verticalOffset() - viewport()->height(); - - if ( verticalScrollMode() == ScrollPerItem ) - { - verticalScrollBar()->setSingleStep ( lastItemRect.height() ); - const int rowsPerPage = qMax ( viewport()->height() / lastItemRect.height(), 1 ); - verticalScrollBar()->setPageStep ( rowsPerPage * lastItemRect.height() ); - } - - verticalScrollBar()->setRange ( 0, bottomRange ); - verticalScrollBar()->setValue ( oldVerticalOffset ); - - //TODO: also consider working with the horizontal scroll bar. since at this level I am not still - // supporting "top to bottom" flow, there is no real problem. If I support that someday - // (think how to draw categories), we would have to take care of the horizontal scroll bar too. - // In theory, as KCategorizedView has been designed, there is no need of horizontal scroll bar. - horizontalScrollBar()->setRange ( 0, 0 ); - - //BEGIN bugs 213068, 287847 ------------------------------------------------------------ - // restoring values from above ... - setVerticalScrollBarPolicy ( verticalP ); - setHorizontalScrollBarPolicy ( horizontalP ); - // ... and correct the visibility - bool validRange = verticalScrollBar()->maximum() != verticalScrollBar()->minimum(); - if ( verticalP == Qt::ScrollBarAsNeeded && ( verticalScrollBar()->isVisibleTo ( this ) != validRange ) ) - verticalScrollBar()->setVisible ( validRange ); - validRange = horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum(); - if ( horizontalP == Qt::ScrollBarAsNeeded && ( horizontalScrollBar()->isVisibleTo ( this ) != validRange ) ) - horizontalScrollBar()->setVisible ( validRange ); - //END bugs 213068, 287847 -------------------------------------------------------------- -} - -void KCategorizedView::currentChanged ( const QModelIndex ¤t, - const QModelIndex &previous ) -{ - QListView::currentChanged ( current, previous ); -} - -void KCategorizedView::dataChanged ( const QModelIndex &topLeft, - const QModelIndex &bottomRight ) -{ - QListView::dataChanged ( topLeft, bottomRight ); - if ( !d->isCategorized() ) - { - return; - } - - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - - //BEGIN: since the model changed data, we need to reconsider item sizes - int i = topLeft.row(); - int indexToCheck = i; - QModelIndex categoryIndex; - QString category; - Private::Block *block; - while ( i <= bottomRight.row() ) - { - const QModelIndex currIndex = d->proxyModel->index ( i, modelColumn(), rootIndex() ); - if ( i == indexToCheck ) - { - categoryIndex = d->proxyModel->index ( i, d->proxyModel->sortColumn(), rootIndex() ); - category = categoryIndex.data ( KCategorizedSortFilterProxyModel::CategoryDisplayRole ).toString(); - block = &d->blocks[category]; - block->quarantineStart = currIndex; - indexToCheck = block->firstIndex.row() + block->items.count(); - } - visualRect ( currIndex ); - ++i; - } - //END: since the model changed data, we need to reconsider item sizes -} - -void KCategorizedView::rowsInserted ( const QModelIndex &parent, - int start, - int end ) -{ - QListView::rowsInserted ( parent, start, end ); - if ( !d->isCategorized() ) - { - return; - } - - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - d->rowsInserted ( parent, start, end ); -} - -void KCategorizedView::slotLayoutChanged() -{ - if ( !d->isCategorized() ) - { - return; - } - - d->blocks.clear(); - *d->hoveredBlock = Private::Block(); - d->hoveredCategory = QString(); - if ( d->proxyModel->rowCount() ) - { - d->rowsInserted ( rootIndex(), 0, d->proxyModel->rowCount() - 1 ); - } -} -//END: Public part - -void KCategorizedView::slotCollapseOrExpandClicked ( QModelIndex idx ) -{ - d->_k_slotCollapseOrExpandClicked ( idx ); -} - - -#include "categorizedview.moc" diff --git a/depends/groupview/src/categorizedview_p.h b/depends/groupview/src/categorizedview_p.h deleted file mode 100644 index 524bba3a..00000000 --- a/depends/groupview/src/categorizedview_p.h +++ /dev/null @@ -1,160 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef KCATEGORIZEDVIEW_P_H -#define KCATEGORIZEDVIEW_P_H - -class KCategorizedSortFilterProxyModel; -class KCategoryDrawer; -class KCategoryDrawerV2; -class KCategoryDrawerV3; - -/** - * @internal - */ -class KCategorizedView::Private -{ -public: - struct Block; - struct Item; - - Private(KCategorizedView *q); - ~Private(); - - /** - * @return whether this view has all required elements to be categorized. - */ - bool isCategorized() const; - - /** - * @return the block rect for the representative @p representative. - */ - QStyleOptionViewItemV4 blockRect(const QModelIndex &representative); - - /** - * Returns the first and last element that intersects with rect. - * - * @note see that here we cannot take out items between first and last (as we could - * do with the rubberband). - * - * Complexity: O(log(n)) where n is model()->rowCount(). - */ - QPair intersectingIndexesWithRect(const QRect &rect) const; - - /** - * Returns the position of the block of @p category. - * - * Complexity: O(n) where n is the number of different categories when the block has been - * marked as in quarantine. O(1) the rest of the times (the vast majority). - */ - QPoint blockPosition(const QString &category); - - /** - * Returns the height of the block determined by @p category. - */ - int blockHeight(const QString &category); - - /** - * Returns the actual viewport width. - */ - int viewportWidth() const; - - /** - * Marks all elements as in quarantine. - * - * Complexity: O(n) where n is model()->rowCount(). - * - * @warning this is an expensive operation - */ - void regenerateAllElements(); - - /** - * Update internal information, and keep sync with the real information that the model contains. - */ - void rowsInserted(const QModelIndex &parent, int start, int end); - - /** - * Returns @p rect in viewport terms, taking in count horizontal and vertical offsets. - */ - QRect mapToViewport(const QRect &rect) const; - - /** - * Returns @p rect in absolute terms, converted from viewport position. - */ - QRect mapFromViewport(const QRect &rect) const; - - /** - * Returns the height of the highest element in last row. This is only applicable if there is - * no grid set and uniformItemSizes is false. - * - * @param block in which block are we searching. Necessary to stop the search if we hit the - * first item in this block. - */ - int highestElementInLastRow(const Block &block) const; - - /** - * Returns whether the view has a valid grid size. - */ - bool hasGrid() const; - - /** - * Returns the category for the given index. - */ - QString categoryForIndex(const QModelIndex &index) const; - - /** - * Updates the visual rect for item when flow is LeftToRight. - */ - void leftToRightVisualRect(const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos) const; - - /** - * Updates the visual rect for item when flow is TopToBottom. - * @note we only support viewMode == ListMode in this case. - */ - void topToBottomVisualRect(const QModelIndex &index, Item &item, - const Block &block, const QPoint &blockPos) const; - - /** - * Called when expand or collapse has been clicked on the category drawer. - */ - void _k_slotCollapseOrExpandClicked(QModelIndex); - - KCategorizedView *q = nullptr; - KCategorizedSortFilterProxyModel *proxyModel = nullptr; - KCategoryDrawer *categoryDrawer = nullptr; - int categorySpacing = 5; - bool alternatingBlockColors = false; - bool collapsibleBlocks = false; - bool constantItemWidth = false; - - // FIXME: this is some really weird logic. Investigate. - Block *hoveredBlock; - QString hoveredCategory; - QModelIndex hoveredIndex; - - QPoint pressedPosition; - QRect rubberBandRect; - - QHash blocks; -}; - -#endif // KCATEGORIZEDVIEW_P_H - diff --git a/depends/groupview/src/categorydrawer.cpp b/depends/groupview/src/categorydrawer.cpp deleted file mode 100644 index 214ce3b2..00000000 --- a/depends/groupview/src/categorydrawer.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007, 2009 Rafael Fernández López - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "categorydrawer.h" - -#include -#include -#include - -#include -#include - -#define HORIZONTAL_HINT 3 - -class KCategoryDrawer::Private -{ -public: - Private(KCategorizedView *view) - : view(view) - , leftMargin(0) - , rightMargin(0) - { - } - - ~Private() - { - } - KCategorizedView *view; - int leftMargin; - int rightMargin; -}; - -KCategoryDrawer::KCategoryDrawer(KCategorizedView *view) - : QObject(view) - , d(new Private(view)) -{ - setLeftMargin(2); - setRightMargin(2); -} - -KCategoryDrawer::~KCategoryDrawer() -{ - delete d; -} - - -void KCategoryDrawer::drawCategory(const QModelIndex &index, - int /*sortRole*/, - const QStyleOption &option, - QPainter *painter) const -{ - painter->setRenderHint(QPainter::Antialiasing); - - const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - const QRect optRect = option.rect; - QFont font(QApplication::font()); - font.setBold(true); - const QFontMetrics fontMetrics = QFontMetrics(font); - - QColor outlineColor = option.palette.text().color(); - outlineColor.setAlphaF(0.35); - - //BEGIN: top left corner - { - painter->save(); - painter->setPen(outlineColor); - const QPointF topLeft(optRect.topLeft()); - QRectF arc(topLeft, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 1440, 1440); - painter->restore(); - } - //END: top left corner - - //BEGIN: left vertical line - { - QPoint start(optRect.topLeft()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topLeft()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: left vertical line - - //BEGIN: horizontal line - { - QPoint start(optRect.topLeft()); - start.rx() += 3; - QPoint horizontalGradTop(optRect.topLeft()); - horizontalGradTop.rx() += optRect.width() - 6; - painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); - } - //END: horizontal line - - //BEGIN: top right corner - { - painter->save(); - painter->setPen(outlineColor); - QPointF topRight(optRect.topRight()); - topRight.rx() -= 4; - QRectF arc(topRight, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 0, 1440); - painter->restore(); - } - //END: top right corner - - //BEGIN: right vertical line - { - QPoint start(optRect.topRight()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topRight()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: right vertical line - - //BEGIN: text - { - QRect textRect(option.rect); - textRect.setTop(textRect.top() + 7); - textRect.setLeft(textRect.left() + 7); - textRect.setHeight(fontMetrics.height()); - textRect.setRight(textRect.right() - 7); - - painter->save(); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category); - painter->restore(); - } - //END: text -} - -int KCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &option) const -{ - Q_UNUSED(index); - Q_UNUSED(option) - - QFont font(QApplication::font()); - font.setBold(true); - QFontMetrics fontMetrics(font); - - const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */ - + 11 /* top and bottom separation */; - return height; -} - -int KCategoryDrawer::leftMargin() const -{ - return d->leftMargin; -} - -void KCategoryDrawer::setLeftMargin(int leftMargin) -{ - d->leftMargin = leftMargin; -} - -int KCategoryDrawer::rightMargin() const -{ - return d->rightMargin; -} - -void KCategoryDrawer::setRightMargin(int rightMargin) -{ - d->rightMargin = rightMargin; -} - -KCategoryDrawer &KCategoryDrawer::operator=(const KCategoryDrawer &cd) -{ - d->leftMargin = cd.d->leftMargin; - d->rightMargin = cd.d->rightMargin; - d->view = cd.d->view; - return *this; -} - -KCategorizedView *KCategoryDrawer::view() const -{ - return d->view; -} - -void KCategoryDrawer::mouseButtonPressed(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawer::mouseButtonReleased(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawer::mouseMoved(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawer::mouseButtonDoubleClicked(const QModelIndex&, const QRect&, QMouseEvent *event) -{ - event->ignore(); -} - -void KCategoryDrawer::mouseLeft(const QModelIndex&, const QRect&) -{ -} - -#include "categorydrawer.moc" From 7839c4ecc01aad4c5d34a6d06b2dfc4afdc58458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 4 Feb 2014 01:40:51 +0100 Subject: [PATCH 39/49] Pave. --- CMakeLists.txt | 19 +- depends/groupview/.clang-format | 24 -- depends/groupview/.gitignore | 2 - depends/groupview/CMakeLists.txt | 40 --- depends/groupview/main.cpp | 98 -------- depends/groupview/main.h | 54 ---- gui/MainWindow.cpp | 30 +-- gui/MainWindow.h | 6 +- gui/dialogs/IconPickerDialog.cpp | 2 +- {depends => gui}/groupview/Group.cpp | 0 {depends => gui}/groupview/Group.h | 0 {depends => gui}/groupview/GroupView.cpp | 0 {depends => gui}/groupview/GroupView.h | 0 .../groupview/GroupedProxyModel.cpp | 7 +- .../groupview/GroupedProxyModel.h | 3 +- .../groupview/InstanceDelegate.cpp | 0 {depends => gui}/groupview/InstanceDelegate.h | 0 gui/widgets/InstanceDelegate.cpp | 231 ------------------ gui/widgets/InstanceDelegate.h | 27 -- logic/lists/InstanceList.cpp | 8 +- logic/lists/InstanceList.h | 4 +- 21 files changed, 41 insertions(+), 514 deletions(-) delete mode 100644 depends/groupview/.clang-format delete mode 100644 depends/groupview/.gitignore delete mode 100644 depends/groupview/CMakeLists.txt delete mode 100644 depends/groupview/main.cpp delete mode 100644 depends/groupview/main.h rename {depends => gui}/groupview/Group.cpp (100%) rename {depends => gui}/groupview/Group.h (100%) rename {depends => gui}/groupview/GroupView.cpp (100%) rename {depends => gui}/groupview/GroupView.h (100%) rename {depends => gui}/groupview/GroupedProxyModel.cpp (73%) rename {depends => gui}/groupview/GroupedProxyModel.h (50%) rename {depends => gui}/groupview/InstanceDelegate.cpp (100%) rename {depends => gui}/groupview/InstanceDelegate.h (100%) delete mode 100644 gui/widgets/InstanceDelegate.cpp delete mode 100644 gui/widgets/InstanceDelegate.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c34f363f..a8adfec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,7 +186,6 @@ configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/includ ADD_DEFINITIONS(-DQUAZIP_STATIC) ADD_DEFINITIONS(-DLIBSETTINGS_STATIC) ADD_DEFINITIONS(-DLIBUTIL_STATIC) -ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC) ######## Packaging/install paths setup ######## @@ -247,10 +246,6 @@ include_directories(${LIBUTIL_INCLUDE_DIR}) add_subdirectory(depends/settings) include_directories(${LIBSETTINGS_INCLUDE_DIR}) -# Add the group view library. -add_subdirectory(depends/groupview) -include_directories(${LIBGROUPVIEW_INCLUDE_DIR}) - # Add the updater add_subdirectory(mmc_updater) @@ -317,8 +312,6 @@ gui/dialogs/UpdateDialog.cpp # GUI - widgets gui/widgets/Common.h gui/widgets/Common.cpp -gui/widgets/InstanceDelegate.h -gui/widgets/InstanceDelegate.cpp gui/widgets/ModListView.h gui/widgets/ModListView.cpp gui/widgets/VersionListView.h @@ -328,6 +321,16 @@ gui/widgets/LabeledToolButton.cpp gui/widgets/MCModInfoFrame.h gui/widgets/MCModInfoFrame.cpp +# GUI - instance group view +gui/groupview/Group.cpp +gui/groupview/Group.h +gui/groupview/GroupedProxyModel.cpp +gui/groupview/GroupedProxyModel.h +gui/groupview/GroupView.cpp +gui/groupview/GroupView.h +gui/groupview/InstanceDelegate.cpp +gui/groupview/InstanceDelegate.h + # Base classes and infrastructure logic/BaseVersion.h logic/MinecraftVersion.h @@ -584,7 +587,7 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS}) # Link TARGET_LINK_LIBRARIES(MultiMC MultiMC_common) -TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS}) +TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings ${MultiMC_LINK_ADDITIONAL_LIBS}) QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES}) QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES}) diff --git a/depends/groupview/.clang-format b/depends/groupview/.clang-format deleted file mode 100644 index 167a8fa7..00000000 --- a/depends/groupview/.clang-format +++ /dev/null @@ -1,24 +0,0 @@ -UseTab: true -IndentWidth: 4 -TabWidth: 4 -ConstructorInitializerIndentWidth: 4 -AccessModifierOffset: -4 -IndentCaseLabels: false -IndentFunctionDeclarationAfterType: false -NamespaceIndentation: None - -BreakBeforeBraces: Allman -AllowShortIfStatementsOnASingleLine: false -ColumnLimit: 96 -MaxEmptyLinesToKeep: 1 - -Standard: Cpp11 -Cpp11BracedListStyle: true - -SpacesInParentheses: false -SpaceInEmptyParentheses: false -SpacesInCStyleCastParentheses: false -SpaceAfterControlStatementKeyword: true - -AlignTrailingComments: true -SpacesBeforeTrailingComments: 1 diff --git a/depends/groupview/.gitignore b/depends/groupview/.gitignore deleted file mode 100644 index a5d18fa3..00000000 --- a/depends/groupview/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build/ -*.user* diff --git a/depends/groupview/CMakeLists.txt b/depends/groupview/CMakeLists.txt deleted file mode 100644 index e2a85950..00000000 --- a/depends/groupview/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 2.8.9) - -project(GroupView) - -set(CMAKE_AUTOMOC ON) - -IF(APPLE) - message(STATUS "Using APPLE CMAKE_CXX_FLAGS") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -ELSEIF(UNIX) - # assume GCC, add C++0x/C++11 stuff - MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -ELSEIF(MINGW) - MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall") -ENDIF() - -find_package(Qt5Core REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Widgets REQUIRED) - -include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS}) - -set(SOURCES - main.cpp - main.h - - GroupView.h - GroupView.cpp - Group.h - Group.cpp - GroupedProxyModel.h - GroupedProxyModel.cpp - InstanceDelegate.h - InstanceDelegate.cpp -) - -add_executable(GroupView ${SOURCES}) -qt5_use_modules(GroupView Core Gui Widgets) diff --git a/depends/groupview/main.cpp b/depends/groupview/main.cpp deleted file mode 100644 index df14e3bd..00000000 --- a/depends/groupview/main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "main.h" - -#include -#include -#include -#include - -#include "GroupView.h" -#include "GroupedProxyModel.h" -#include "InstanceDelegate.h" - -Progresser *progresser; - -QPixmap icon(const Qt::GlobalColor color) -{ - QPixmap p = QPixmap(32, 32); - p.fill(QColor(color)); - return p; -} -QPixmap icon(const int number) -{ - QPixmap p = icon(Qt::white); - QPainter painter(&p); - QFont font = painter.font(); - font.setBold(true); - font.setPixelSize(28); - painter.setFont(font); - painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter, - QString::number(number)); - painter.end(); - return p; -} -QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, - const QString &category) -{ - QStandardItem *item = new QStandardItem; - item->setText(text); - item->setData(icon(color), Qt::DecorationRole); - item->setData(category, GroupViewRoles::GroupRole); - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - // progresser->addTrackedIndex(item); - return item; -} -QStandardItem *createItem(const int index, const QString &category) -{ - QStandardItem *item = new QStandardItem; - item->setText(QString("Item #%1").arg(index)); - item->setData(icon(index), Qt::DecorationRole); - item->setData(category, GroupViewRoles::GroupRole); - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - // progresser->addTrackedIndex(item); - return item; -} - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - - qsrand(QTime::currentTime().msec()); - - progresser = new Progresser(); - - QStandardItemModel model; - model.setRowCount(10); - model.setColumnCount(1); - - model.setItem( - 0, createItem(Qt::red, - "Red is a color. Some more text. I'm out of ideas. 42. What's your name?", - "Colorful")); - model.setItem(1, createItem(Qt::blue, "Blue", "Colorful")); - model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful")); - - model.setItem(3, createItem(Qt::black, "Black", "Not Colorful")); - model.setItem(4, createItem(Qt::darkGray, "Dark Gray", "Not Colorful")); - model.setItem(5, createItem(Qt::gray, "Gray", "Not Colorful")); - model.setItem(6, createItem(Qt::lightGray, "Light Gray", "Not Colorful")); - model.setItem(7, createItem(Qt::white, "White", "Not Colorful")); - - model.setItem(8, createItem(Qt::darkGreen, "Dark Green", "")); - model.setItem(9, progresser->addTrackedIndex(createItem(Qt::green, "Green", ""))); - - for (int i = 0; i < 20; ++i) - { - model.setItem(i + 10, createItem(i + 1, "Items 1-20")); - } - - GroupedProxyModel pModel; - pModel.setSourceModel(&model); - - GroupView w; - w.setItemDelegate(new ListViewDelegate); - w.setModel(&pModel); - w.resize(640, 480); - w.show(); - - return a.exec(); -} diff --git a/depends/groupview/main.h b/depends/groupview/main.h deleted file mode 100644 index 2a358329..00000000 --- a/depends/groupview/main.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "GroupView.h" - -class Progresser : public QObject -{ - Q_OBJECT -public: - explicit Progresser(QObject *parent = 0) : QObject(parent) - { - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); - timer->start(50); - } - - QStandardItem *addTrackedIndex(QStandardItem *item) - { - item->setData(1000, GroupViewRoles::ProgressMaximumRole); - m_items.append(item); - return item; - } - -public -slots: - void timeout() - { - QList toRemove; - for (auto item : m_items) - { - int maximum = item->data(GroupViewRoles::ProgressMaximumRole).toInt(); - int value = item->data(GroupViewRoles::ProgressValueRole).toInt(); - int newvalue = std::min(value + 3, maximum); - item->setData(newvalue, GroupViewRoles::ProgressValueRole); - - if(newvalue >= maximum) - { - toRemove.append(item); - } - } - for(auto remove : toRemove) - { - m_items.removeAll(remove); - } - } - -private: - QList m_items; -}; diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 9977dc75..cd8ec926 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -37,12 +38,12 @@ #include "userutils.h" #include "pathutils.h" -#include "categorizedview.h" -#include "categorydrawer.h" +#include "gui/groupview/GroupView.h" +#include "gui/groupview/InstanceDelegate.h" #include "gui/Platform.h" -#include "gui/widgets/InstanceDelegate.h" + #include "gui/widgets/LabeledToolButton.h" #include "gui/dialogs/SettingsDialog.h" @@ -140,21 +141,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi // Create the instance list widget { - view = new KCategorizedView(ui->centralWidget); - drawer = new KCategoryDrawer(view); + view = new GroupView(ui->centralWidget); + view->setSelectionMode(QAbstractItemView::SingleSelection); - view->setCategoryDrawer(drawer); - view->setCollapsibleBlocks(true); - view->setViewMode(QListView::IconMode); - view->setFlow(QListView::LeftToRight); - view->setWordWrap(true); - view->setMouseTracking(true); - view->viewport()->setAttribute(Qt::WA_Hover); + // view->setCategoryDrawer(drawer); + // view->setCollapsibleBlocks(true); + // view->setViewMode(QListView::IconMode); + // view->setFlow(QListView::LeftToRight); + // view->setWordWrap(true); + // view->setMouseTracking(true); + // view->viewport()->setAttribute(Qt::WA_Hover); auto delegate = new ListViewDelegate(); view->setItemDelegate(delegate); - view->setSpacing(10); - view->setUniformItemWidths(true); + // view->setSpacing(10); + // view->setUniformItemWidths(true); // do not show ugly blue border on the mac view->setAttribute(Qt::WA_MacShowFocusRect, false); @@ -331,7 +332,6 @@ MainWindow::~MainWindow() { delete ui; delete proxymodel; - delete drawer; } void MainWindow::showInstanceContextMenu(const QPoint &pos) diff --git a/gui/MainWindow.h b/gui/MainWindow.h index eeba2c26..4d9e165d 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -27,9 +27,6 @@ class QToolButton; class LabeledToolButton; class QLabel; -class InstanceProxyModel; -class KCategorizedView; -class KCategoryDrawer; class MinecraftProcess; class ConsoleWindow; @@ -185,8 +182,7 @@ protected: private: Ui::MainWindow *ui; - KCategoryDrawer *drawer; - KCategorizedView *view; + class GroupView *view; InstanceProxyModel *proxymodel; MinecraftProcess *proc; ConsoleWindow *console; diff --git a/gui/dialogs/IconPickerDialog.cpp b/gui/dialogs/IconPickerDialog.cpp index f7970b37..9b1c26ff 100644 --- a/gui/dialogs/IconPickerDialog.cpp +++ b/gui/dialogs/IconPickerDialog.cpp @@ -23,7 +23,7 @@ #include "ui_IconPickerDialog.h" #include "gui/Platform.h" -#include "gui/widgets/InstanceDelegate.h" +#include "gui/groupview/InstanceDelegate.h" #include "logic/icons/IconList.h" diff --git a/depends/groupview/Group.cpp b/gui/groupview/Group.cpp similarity index 100% rename from depends/groupview/Group.cpp rename to gui/groupview/Group.cpp diff --git a/depends/groupview/Group.h b/gui/groupview/Group.h similarity index 100% rename from depends/groupview/Group.h rename to gui/groupview/Group.h diff --git a/depends/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp similarity index 100% rename from depends/groupview/GroupView.cpp rename to gui/groupview/GroupView.cpp diff --git a/depends/groupview/GroupView.h b/gui/groupview/GroupView.h similarity index 100% rename from depends/groupview/GroupView.h rename to gui/groupview/GroupView.h diff --git a/depends/groupview/GroupedProxyModel.cpp b/gui/groupview/GroupedProxyModel.cpp similarity index 73% rename from depends/groupview/GroupedProxyModel.cpp rename to gui/groupview/GroupedProxyModel.cpp index 57d7ff5c..d9d6ac78 100644 --- a/depends/groupview/GroupedProxyModel.cpp +++ b/gui/groupview/GroupedProxyModel.cpp @@ -12,10 +12,15 @@ bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString(); if (leftCategory == rightCategory) { - return left.row() < right.row(); + return subSortLessThan(left, right); } else { return leftCategory < rightCategory; } } + +bool GroupedProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const +{ + return left.row() < right.row(); +} diff --git a/depends/groupview/GroupedProxyModel.h b/gui/groupview/GroupedProxyModel.h similarity index 50% rename from depends/groupview/GroupedProxyModel.h rename to gui/groupview/GroupedProxyModel.h index cae87ecd..12edee0f 100644 --- a/depends/groupview/GroupedProxyModel.h +++ b/gui/groupview/GroupedProxyModel.h @@ -10,5 +10,6 @@ public: GroupedProxyModel(QObject *parent = 0); protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const; }; diff --git a/depends/groupview/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp similarity index 100% rename from depends/groupview/InstanceDelegate.cpp rename to gui/groupview/InstanceDelegate.cpp diff --git a/depends/groupview/InstanceDelegate.h b/gui/groupview/InstanceDelegate.h similarity index 100% rename from depends/groupview/InstanceDelegate.h rename to gui/groupview/InstanceDelegate.h diff --git a/gui/widgets/InstanceDelegate.cpp b/gui/widgets/InstanceDelegate.cpp deleted file mode 100644 index 33da7130..00000000 --- a/gui/widgets/InstanceDelegate.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * 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 "InstanceDelegate.h" -#include -#include -#include -#include -#include -#include "Common.h" -#define QFIXED_MAX (INT_MAX / 256) - -ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) -{ -} - -void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, - const QRect &rect) -{ - 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) -{ - if (!(option.state & QStyle::State_HasFocus)) - return; - QStyleOptionFocusRect opt; - opt.direction = option.direction; - opt.fontMetrics = option.fontMetrics; - opt.palette = option.palette; - opt.rect = rect; - // opt.state = option.state | QStyle::State_KeyboardFocusChange | - // QStyle::State_Item; - auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base; - opt.backgroundColor = option.palette.color(col); - // Apparently some widget styles expect this hint to not be set - painter->setRenderHint(QPainter::Antialiasing, false); - - QStyle *style = option.widget ? option.widget->style() : QApplication::style(); - - style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); - - painter->setRenderHint(QPainter::Antialiasing); -} - -static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option) -{ - QStyle *style = option->widget ? option->widget->style() : QApplication::style(); - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(option->font); - textLayout.setText(option->text); - const int textMargin = - style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1; - QRect bounds(0, 0, 100 - 2 * textMargin, 600); - qreal height = 0, widthUsed = 0; - viewItemTextLayout(textLayout, bounds.width(), height, widthUsed); - const QSize size(qCeil(widthUsed), qCeil(height)); - return QSize(size.width() + 2 * textMargin, size.height()); -} - -void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - painter->save(); - painter->setClipRect(opt.rect); - - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - - // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); - const int iconSize = 48; - QRect iconbox = opt.rect; - const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; - QRect textRect = opt.rect; - QRect textHighlightRect = textRect; - // clip the decoration on top, remove width padding - textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0); - - textHighlightRect.adjust(0, iconSize + 5, 0, 0); - - // draw background - { - // FIXME: unused - // QSize textSize = viewItemTextSize ( &opt ); - QPalette::ColorGroup cg; - QStyleOptionViewItemV4 opt2(opt); - - if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) - { - if (!(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - else - cg = QPalette::Normal; - } - else - { - cg = QPalette::Disabled; - } - opt2.palette.setCurrentColorGroup(cg); - - // fill in background, if any - if (opt.backgroundBrush.style() != Qt::NoBrush) - { - QPointF oldBO = painter->brushOrigin(); - painter->setBrushOrigin(opt.rect.topLeft()); - painter->fillRect(opt.rect, opt.backgroundBrush); - painter->setBrushOrigin(oldBO); - } - - if (opt.showDecorationSelected) - { - drawSelectionRect(painter, opt2, opt.rect); - drawFocusRect(painter, opt2, opt.rect); - // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); - } - else - { - - // if ( opt.state & QStyle::State_Selected ) - { - // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, - // opt.widget ); - // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, - // QPalette::Highlight ) ); - drawSelectionRect(painter, opt2, textHighlightRect); - drawFocusRect(painter, opt2, textHighlightRect); - } - } - } - - // draw the icon - { - QIcon::Mode mode = QIcon::Normal; - if (!(opt.state & QStyle::State_Enabled)) - mode = QIcon::Disabled; - else if (opt.state & QStyle::State_Selected) - mode = QIcon::Selected; - QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; - - iconbox.setHeight(iconSize); - opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); - } - // set the text colors - QPalette::ColorGroup cg = - opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - if (opt.state & QStyle::State_Selected) - { - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - } - else - { - painter->setPen(opt.palette.color(cg, QPalette::Text)); - } - - // draw the text - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - textOption.setTextDirection(opt.direction); - textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment)); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(opt.font); - textLayout.setText(opt.text); - - qreal width, height; - viewItemTextLayout(textLayout, textRect.width(), height, width); - - const int lineCount = textLayout.lineCount(); - - const QRect layoutRect = QStyle::alignedRect( - opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); - const QPointF position = layoutRect.topLeft(); - for (int i = 0; i < lineCount; ++i) - { - const QTextLine line = textLayout.lineAt(i); - line.draw(painter, position); - } - - painter->restore(); -} - -QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - const int textMargin = - style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; - int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables - QSize szz = viewItemTextSize(&opt); - height += szz.height(); - // FIXME: maybe the icon items could scale and keep proportions? - QSize sz(100, height); - return sz; -} diff --git a/gui/widgets/InstanceDelegate.h b/gui/widgets/InstanceDelegate.h deleted file mode 100644 index 6f924405..00000000 --- a/gui/widgets/InstanceDelegate.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * 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 - -class ListViewDelegate : public QStyledItemDelegate -{ -public: - explicit ListViewDelegate ( QObject* parent = 0 ); -protected: - void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; - QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; -}; diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 0d4eab95..935f9fd3 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -33,6 +33,7 @@ #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logger/QsLog.h" +#include const static int GROUP_FILE_FORMAT_VERSION = 1; @@ -96,8 +97,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const return MMC->icons()->getIcon(key); } // for now. - case KCategorizedSortFilterProxyModel::CategorySortRole: - case KCategorizedSortFilterProxyModel::CategoryDisplayRole: + case GroupViewRoles::GroupRole: { return pdata->group(); } @@ -585,10 +585,8 @@ void InstanceList::propertiesChanged(BaseInstance *inst) } InstanceProxyModel::InstanceProxyModel(QObject *parent) - : KCategorizedSortFilterProxyModel(parent) + : GroupedProxyModel(parent) { - // disable since by default we are globally sorting by date: - setCategorizedModel(true); } bool InstanceProxyModel::subSortLessThan(const QModelIndex &left, diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index 0ce808e5..cda51a03 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -18,7 +18,7 @@ #include #include #include -#include "categorizedsortfilterproxymodel.h" +#include #include #include "logic/BaseInstance.h" @@ -129,7 +129,7 @@ protected: QSet m_groups; }; -class InstanceProxyModel : public KCategorizedSortFilterProxyModel +class InstanceProxyModel : public GroupedProxyModel { public: explicit InstanceProxyModel(QObject *parent = 0); From 6206a241ea51864b22d493d5b7b0a9282c754571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 4 Feb 2014 02:01:11 +0100 Subject: [PATCH 40/49] A try at fixing the instance delegate. A bit. Maybe. --- gui/groupview/InstanceDelegate.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gui/groupview/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp index 8527e2bc..8a273758 100644 --- a/gui/groupview/InstanceDelegate.cpp +++ b/gui/groupview/InstanceDelegate.cpp @@ -173,6 +173,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti opt2.palette.setCurrentColorGroup(cg); // fill in background, if any + if (opt.backgroundBrush.style() != Qt::NoBrush) { QPointF oldBO = painter->brushOrigin(); @@ -181,6 +182,9 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti painter->setBrushOrigin(oldBO); } + drawSelectionRect(painter, opt2, textHighlightRect); + + /* if (opt.showDecorationSelected) { drawSelectionRect(painter, opt2, opt.rect); @@ -200,6 +204,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti drawFocusRect(painter, opt2, textHighlightRect); } } + */ } // draw the icon From c84c51860d4a39f9219998826c093e4e60b7bf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 4 Feb 2014 21:18:02 +0100 Subject: [PATCH 41/49] Fix crash bug related to data changes in new group view. --- gui/groupview/GroupView.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp index 89e3e223..fb4b45f4 100644 --- a/gui/groupview/GroupView.cpp +++ b/gui/groupview/GroupView.cpp @@ -48,10 +48,14 @@ GroupView::~GroupView() void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { + /* if (roles.contains(GroupViewRoles::GroupRole) || roles.contains(Qt::DisplayRole)) { + */ updateGeometries(); + /* } + */ viewport()->update(); } void GroupView::rowsInserted(const QModelIndex &parent, int start, int end) From 573d4c8050fa6548e4680fd74c1a0872e9a62c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 5 Feb 2014 01:34:50 +0100 Subject: [PATCH 42/49] Paint the headers nicer. --- gui/groupview/Group.cpp | 66 ++++++++++++++++++++++++------------- gui/groupview/Group.h | 3 +- gui/groupview/GroupView.cpp | 9 ++--- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/gui/groupview/Group.cpp b/gui/groupview/Group.cpp index f216cc6e..7cce674a 100644 --- a/gui/groupview/Group.cpp +++ b/gui/groupview/Group.cpp @@ -35,18 +35,18 @@ Group::HitResults Group::hitScan(const QPoint &pos) const int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? int y = pos.y(); // int x = pos.x(); - if(y < y_start) + if (y < y_start) { results = Group::NoHit; } - else if(y < body_start) + else if (y < body_start) { results = Group::HeaderHit; int collapseSize = headerHeight() - 4; // the icon QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); - if(iconRect.contains(pos)) + if (iconRect.contains(pos)) { results |= Group::CheckboxHit; } @@ -58,37 +58,53 @@ Group::HitResults Group::hitScan(const QPoint &pos) const return results; } -void Group::drawHeader(QPainter *painter, const int y) +void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option, const int y) { + QStyleOptionViewItemV4 opt = option; painter->save(); - int height = headerHeight() - 4; - int collapseSize = height; - - // the icon - QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y, collapseSize, collapseSize); - painter->setPen(QPen(Qt::black, 1)); - painter->drawRect(iconRect); static const int margin = 2; + static const int spacing = 10; + int height = headerHeight(); + int text_height = height - 2 * margin; + + // set the text colors + QPalette::ColorGroup cg = QPalette::Normal; + painter->setPen(opt.palette.color(cg, QPalette::Text)); + + // set up geometry + QRect iconRect = QRect(view->m_leftMargin + margin, y + margin, text_height - 1, text_height - 1); QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); + QRect smallRect = iconSubrect.adjusted(margin, margin, -margin, -margin); int midX = iconSubrect.center().x(); int midY = iconSubrect.center().y(); - if (collapsed) + + // checkboxy thingy { - painter->drawLine(midX, iconSubrect.top(), midX, iconSubrect.bottom()); + painter->drawRect(iconSubrect); + + painter->setBrush(opt.palette.text()); + painter->drawRect(smallRect.x(), midY, smallRect.width(), 2); + if(collapsed) + { + painter->drawRect(midX, smallRect.y(), 2, smallRect.height()); + } } - painter->drawLine(iconSubrect.left(), midY, iconSubrect.right(), midY); - // the text - int textWidth = painter->fontMetrics().width(text); - QRect textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); - painter->setBrush(view->viewOptions().palette.text()); - view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, - view->viewport()->palette(), true, text); + int x_left = iconRect.right(); + if(text.length()) + { + // the text + int text_width = painter->fontMetrics().width(text); + QRect textRect = QRect(x_left + spacing, y + margin, text_width, text_height); + x_left = textRect.right(); + view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, + opt.palette, true, text); + } // the line - painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, - view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); + painter->drawLine(x_left + spacing, midY + 1, view->contentWidth() - view->m_rightMargin, + midY + 1); painter->restore(); } @@ -100,7 +116,11 @@ int Group::totalHeight() const int Group::headerHeight() const { - return view->viewport()->fontMetrics().height() + 4; + int raw = view->viewport()->fontMetrics().height() + 4; + // add english. maybe. depends on font height. + if(raw % 2 == 0) + raw++; + return std::min( raw , 25 ); } int Group::contentHeight() const diff --git a/gui/groupview/Group.h b/gui/groupview/Group.h index 455ee1a8..46a12f63 100644 --- a/gui/groupview/Group.h +++ b/gui/groupview/Group.h @@ -3,6 +3,7 @@ #include #include #include +#include class GroupView; class QPainter; @@ -26,7 +27,7 @@ struct Group void update(); /// draw the header at y-position. - void drawHeader(QPainter *painter, const int y); + void drawHeader(QPainter *painter, const QStyleOptionViewItem &option, const int y); /// height of the group, in total. includes a small bit of padding. int totalHeight() const; diff --git a/gui/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp index fb4b45f4..9e186854 100644 --- a/gui/groupview/GroupView.cpp +++ b/gui/groupview/GroupView.cpp @@ -206,7 +206,7 @@ int GroupView::categoryRowHeight(const QModelIndex &index) const largestHeight = qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); } - return largestHeight; + return largestHeight + m_spacing; } QPair GroupView::categoryInternalPosition(const QModelIndex &index) const @@ -423,11 +423,14 @@ void GroupView::paintEvent(QPaintEvent *event) { QPainter painter(this->viewport()); + QStyleOptionViewItemV4 option(viewOptions()); + option.widget = this; + int y = -verticalOffset(); for (int i = 0; i < m_groups.size(); ++i) { Group *category = m_groups.at(i); - category->drawHeader(&painter, y); + category->drawHeader(&painter, option, y); y += category->totalHeight() + m_categoryMargin; } @@ -439,9 +442,7 @@ void GroupView::paintEvent(QPaintEvent *event) continue; } Qt::ItemFlags flags = index.flags(); - QStyleOptionViewItemV4 option(viewOptions()); option.rect = visualRect(index); - option.widget = this; option.features |= QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway? if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) From 6d9819cccf3809edef2f6a1aeb0c6d01d6067501 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 8 Feb 2014 12:47:14 +0100 Subject: [PATCH 43/49] Error if a patch file is for a different version of minecraft --- logic/OneSixVersionBuilder.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 03555070..62a5ca43 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -538,6 +538,15 @@ struct VersionFile void applyTo(OneSixVersion *version, bool &isError) { isError = true; + if (!version->id.isNull() && !mcVersion.isNull()) + { + if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) == -1) + { + QLOG_ERROR() << filename << "is for a different version of Minecraft"; + return; + } + } + if (!id.isNull()) { version->id = id; From 53069205faf4063e838bab4beea5089d6ec2a0af Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Sat, 8 Feb 2014 17:22:26 +0100 Subject: [PATCH 44/49] Allow overriding the order in which patches are applied --- gui/dialogs/OneSixModEditDialog.cpp | 122 ++++++++++++++++++++++++++++ gui/dialogs/OneSixModEditDialog.h | 6 ++ gui/dialogs/OneSixModEditDialog.ui | 28 +++++++ logic/OneSixVersion.cpp | 9 ++ logic/OneSixVersion.h | 3 + logic/OneSixVersionBuilder.cpp | 68 ++++++++++++++++ logic/OneSixVersionBuilder.h | 4 + 7 files changed, 240 insertions(+) diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp index 1b61771d..9e585de5 100644 --- a/gui/dialogs/OneSixModEditDialog.cpp +++ b/gui/dialogs/OneSixModEditDialog.cpp @@ -39,6 +39,18 @@ #include "logic/lists/ForgeVersionList.h" #include "logic/ForgeInstaller.h" #include "logic/LiteLoaderInstaller.h" +#include "logic/OneSixVersionBuilder.h" + +template +QMap invert(const QMap &in) +{ + QMap out; + for (auto it = in.begin(); it != in.end(); ++it) + { + out.insert(it.value(), it.key()); + } + return out; +} OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) : QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst) @@ -129,6 +141,87 @@ void OneSixModEditDialog::on_removeLibraryBtn_clicked() } } +void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked() +{ + QDir(m_inst->instanceRoot()).remove("order.json"); + m_inst->reloadVersion(this); +} +void OneSixModEditDialog::on_moveLibraryUpBtn_clicked() +{ + + QMap order = getExistingOrder(); + if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty()) + { + return; + } + const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row(); + const QString ourId = m_version->versionFileId(ourRow); + const int ourOrder = order[ourId]; + if (ourId.isNull() || ourId.startsWith("org.multimc.")) + { + return; + } + + QMap sortedOrder = invert(order); + + QList sortedOrders = sortedOrder.keys(); + const int ourIndex = sortedOrders.indexOf(ourOrder); + if (ourIndex <= 0) + { + return; + } + const int ourNewOrder = sortedOrders.at(ourIndex - 1); + order[ourId] = ourNewOrder; + order[sortedOrder[sortedOrders[ourIndex - 1]]] = ourOrder; + + if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst)) + { + QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order")); + } + else + { + m_inst->reloadVersion(this); + ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow - 1), QItemSelectionModel::SelectCurrent); + } +} +void OneSixModEditDialog::on_moveLibraryDownBtn_clicked() +{ + QMap order = getExistingOrder(); + if (order.size() < 2 || ui->libraryTreeView->selectionModel()->selectedIndexes().isEmpty()) + { + return; + } + const int ourRow = ui->libraryTreeView->selectionModel()->selectedIndexes().first().row(); + const QString ourId = m_version->versionFileId(ourRow); + const int ourOrder = order[ourId]; + if (ourId.isNull() || ourId.startsWith("org.multimc.")) + { + return; + } + + QMap sortedOrder = invert(order); + + QList sortedOrders = sortedOrder.keys(); + const int ourIndex = sortedOrders.indexOf(ourOrder); + if ((ourIndex + 1) >= sortedOrders.size()) + { + return; + } + const int ourNewOrder = sortedOrders.at(ourIndex + 1); + order[ourId] = ourNewOrder; + order[sortedOrder[sortedOrders[ourIndex + 1]]] = ourOrder; + + if (!OneSixVersionBuilder::writeOverrideOrders(order, m_inst)) + { + QMessageBox::critical(this, tr("Error"), tr("Couldn't save the new order")); + } + else + { + m_inst->reloadVersion(this); + ui->libraryTreeView->selectionModel()->select(m_version->index(ourRow + 1), QItemSelectionModel::SelectCurrent); + } +} + void OneSixModEditDialog::on_forgeBtn_clicked() { if (QDir(m_inst->instanceRoot()).exists("custom.json")) @@ -250,6 +343,35 @@ bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) return QDialog::eventFilter(ui->resPackTreeView, keyEvent); } +QMap OneSixModEditDialog::getExistingOrder() const +{ + + QMap order; + // default + { + for (OneSixVersion::VersionFile file : m_version->versionFiles) + { + if (file.id.startsWith("org.multimc.")) + { + continue; + } + order.insert(file.id, file.order); + } + } + // overriden + { + QMap overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_inst); + for (auto id : order.keys()) + { + if (overridenOrder.contains(id)) + { + order[id] = overridenOrder[id]; + } + } + } + return order; +} + bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() != QEvent::KeyPress) diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h index a7def967..f44b336b 100644 --- a/gui/dialogs/OneSixModEditDialog.h +++ b/gui/dialogs/OneSixModEditDialog.h @@ -47,6 +47,9 @@ slots: void on_liteloaderBtn_clicked(); void on_reloadLibrariesBtn_clicked(); void on_removeLibraryBtn_clicked(); + void on_resetLibraryOrderBtn_clicked(); + void on_moveLibraryUpBtn_clicked(); + void on_moveLibraryDownBtn_clicked(); void updateVersionControls(); void disableVersionControls(); @@ -62,6 +65,9 @@ private: std::shared_ptr m_resourcepacks; EnabledItemFilter *main_model; OneSixInstance *m_inst; + + QMap getExistingOrder() const; + public slots: void loaderCurrent(QModelIndex current, QModelIndex previous); diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui index b97fa323..eaf8f7fd 100644 --- a/gui/dialogs/OneSixModEditDialog.ui +++ b/gui/dialogs/OneSixModEditDialog.ui @@ -108,6 +108,34 @@ + + + + Reset order + + + + + + + Qt::Horizontal + + + + + + + Move up + + + + + + + Move down + + + diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index e19683d8..fb32f3a8 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -81,6 +81,15 @@ bool OneSixVersion::canRemove(const int index) const return false; } +QString OneSixVersion::versionFileId(const int index) const +{ + if (index < 0 || index >= versionFiles.size()) + { + return QString(); + } + return versionFiles.at(index).id; +} + bool OneSixVersion::remove(const int index) { if (canRemove(index)) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 516f153c..ba7695d5 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -44,6 +44,8 @@ public: bool canRemove(const int index) const; + QString versionFileId(const int index) const; + public slots: bool remove(const int index); @@ -123,6 +125,7 @@ public: QString version; QString mcVersion; QString filename; + int order; }; QList versionFiles; diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 62a5ca43..bbd33ddc 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -763,6 +763,7 @@ struct VersionFile versionFile.version = this->version; versionFile.mcVersion = mcVersion; versionFile.filename = filename; + versionFile.order = order; version->versionFiles.append(versionFile); isError = false; @@ -858,6 +859,7 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla) // patches/ { // load all, put into map for ordering, apply in the right order + QMap overrideOrder = readOverrideOrders(m_instance); QMap> files; for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) @@ -868,6 +870,15 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla) { return false; } + if (overrideOrder.contains(file.fileId)) + { + file.order = overrideOrder.value(file.fileId); + } + if (files.contains(file.order)) + { + QLOG_ERROR() << file.fileId << "has the same order as" << files[file.order].second.fileId; + return false; + } files.insert(file.order, qMakePair(info.fileName(), file)); } for (auto order : files.keys()) @@ -1007,3 +1018,60 @@ bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrd } return true; } + +QMap OneSixVersionBuilder::readOverrideOrders(OneSixInstance *instance) +{ + QMap out; + if (QDir(instance->instanceRoot()).exists("order.json")) + { + QFile orderFile(instance->instanceRoot() + "/order.json"); + if (!orderFile.open(QFile::ReadOnly)) + { + QLOG_ERROR() << "Couldn't open" << orderFile.fileName() << " for reading:" << orderFile.errorString(); + QLOG_WARN() << "Ignoring overriden order"; + } + else + { + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error); + if (error.error != QJsonParseError::NoError || !doc.isObject()) + { + QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString(); + QLOG_WARN() << "Ignoring overriden order"; + } + else + { + QJsonObject obj = doc.object(); + for (auto it = obj.begin(); it != obj.end(); ++it) + { + if (it.key().startsWith("org.multimc.")) + { + continue; + } + out.insert(it.key(), it.value().toDouble()); + } + } + } + } + return out; +} +bool OneSixVersionBuilder::writeOverrideOrders(const QMap &order, OneSixInstance *instance) +{ + QJsonObject obj; + for (auto it = order.cbegin(); it != order.cend(); ++it) + { + if (it.key().startsWith("org.multimc.")) + { + continue; + } + obj.insert(it.key(), it.value()); + } + QFile orderFile(instance->instanceRoot() + "/order.json"); + if (!orderFile.open(QFile::WriteOnly)) + { + QLOG_ERROR() << "Couldn't open" << orderFile.fileName() << "for writing:" << orderFile.errorString(); + return false; + } + orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented)); + return true; +} diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index f0d69bf6..ab0966df 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -16,6 +16,7 @@ #pragma once #include +#include class OneSixVersion; class OneSixInstance; @@ -30,6 +31,8 @@ class OneSixVersionBuilder public: static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla); static bool read(OneSixVersion *version, const QJsonObject &obj); + static QMap readOverrideOrders(OneSixInstance *instance); + static bool writeOverrideOrders(const QMap &order, OneSixInstance *instance); private: OneSixVersion *m_version; @@ -40,4 +43,5 @@ private: bool read(const QJsonObject &obj); bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out); + }; From aa41b891f06a629099d108ab1f4df94d1bbc828c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 8 Feb 2014 21:46:29 +0100 Subject: [PATCH 45/49] Group View: Use painting code from the previous group headers, small optimizations --- gui/groupview/Group.cpp | 105 +++++++++++++++++++++++++++++++----- gui/groupview/Group.h | 9 ++-- gui/groupview/GroupView.cpp | 22 ++++++-- 3 files changed, 117 insertions(+), 19 deletions(-) diff --git a/gui/groupview/Group.cpp b/gui/groupview/Group.cpp index 7cce674a..f3319dc3 100644 --- a/gui/groupview/Group.cpp +++ b/gui/groupview/Group.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "GroupView.h" @@ -58,8 +59,9 @@ Group::HitResults Group::hitScan(const QPoint &pos) const return results; } -void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option, const int y) +void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) { + /* QStyleOptionViewItemV4 opt = option; painter->save(); @@ -107,6 +109,95 @@ void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option, co midY + 1); painter->restore(); + */ + painter->setRenderHint(QPainter::Antialiasing); + + const QRect optRect = option.rect; + QFont font(QApplication::font()); + font.setBold(true); + const QFontMetrics fontMetrics = QFontMetrics(font); + + QColor outlineColor = option.palette.text().color(); + outlineColor.setAlphaF(0.35); + + //BEGIN: top left corner + { + painter->save(); + painter->setPen(outlineColor); + const QPointF topLeft(optRect.topLeft()); + QRectF arc(topLeft, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 1440, 1440); + painter->restore(); + } + //END: top left corner + + //BEGIN: left vertical line + { + QPoint start(optRect.topLeft()); + start.ry() += 3; + QPoint verticalGradBottom(optRect.topLeft()); + verticalGradBottom.ry() += fontMetrics.height() + 5; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); + } + //END: left vertical line + + //BEGIN: horizontal line + { + QPoint start(optRect.topLeft()); + start.rx() += 3; + QPoint horizontalGradTop(optRect.topLeft()); + horizontalGradTop.rx() += optRect.width() - 6; + painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); + } + //END: horizontal line + + //BEGIN: top right corner + { + painter->save(); + painter->setPen(outlineColor); + QPointF topRight(optRect.topRight()); + topRight.rx() -= 4; + QRectF arc(topRight, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 0, 1440); + painter->restore(); + } + //END: top right corner + + //BEGIN: right vertical line + { + QPoint start(optRect.topRight()); + start.ry() += 3; + QPoint verticalGradBottom(optRect.topRight()); + verticalGradBottom.ry() += fontMetrics.height() + 5; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); + } + //END: right vertical line + + //BEGIN: text + { + QRect textRect(option.rect); + textRect.setTop(textRect.top() + 7); + textRect.setLeft(textRect.left() + 7); + textRect.setHeight(fontMetrics.height()); + textRect.setRight(textRect.right() - 7); + + painter->save(); + painter->setFont(font); + QColor penColor(option.palette.text().color()); + penColor.setAlphaF(0.6); + painter->setPen(penColor); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); + painter->restore(); + } + //END: text } int Group::totalHeight() const @@ -144,17 +235,7 @@ int Group::numRows() const int Group::verticalPosition() const { - int res = 0; - const QList cats = view->m_groups; - for (int i = 0; i < cats.size(); ++i) - { - if (cats.at(i) == this) - { - break; - } - res += cats.at(i)->totalHeight() + view->m_categoryMargin; - } - return res; + return m_verticalPosition; } QList Group::items() const diff --git a/gui/groupview/Group.h b/gui/groupview/Group.h index 46a12f63..3b797f4c 100644 --- a/gui/groupview/Group.h +++ b/gui/groupview/Group.h @@ -16,18 +16,19 @@ struct Group Group(const Group *other); /* data */ - GroupView *view; + GroupView *view = nullptr; QString text; - bool collapsed; + bool collapsed = false; QVector rowHeights; - int firstItemIndex; + int firstItemIndex = 0; + int m_verticalPosition = 0; /* logic */ /// do stuff. and things. TODO: redo. void update(); /// draw the header at y-position. - void drawHeader(QPainter *painter, const QStyleOptionViewItem &option, const int y); + void drawHeader(QPainter *painter, const QStyleOptionViewItem &option); /// height of the group, in total. includes a small bit of padding. int totalHeight() const; diff --git a/gui/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp index 9e186854..9954d743 100644 --- a/gui/groupview/GroupView.cpp +++ b/gui/groupview/GroupView.cpp @@ -114,12 +114,19 @@ void GroupView::updateGeometries() else { int totalHeight = 0; + // top margin + totalHeight += m_categoryMargin; for (auto category : m_groups) { + category->m_verticalPosition = totalHeight; totalHeight += category->totalHeight() + m_categoryMargin; } + /* // remove the last margin (we don't want it) totalHeight -= m_categoryMargin; + // add a margin on top... + totalHeight += m_categoryMargin; + */ totalHeight += m_bottomMargin; verticalScrollBar()->setRange(0, totalHeight - height()); } @@ -426,12 +433,22 @@ void GroupView::paintEvent(QPaintEvent *event) QStyleOptionViewItemV4 option(viewOptions()); option.widget = this; - int y = -verticalOffset(); + int wpWidth = viewport()->width(); + option.rect.setWidth(wpWidth); for (int i = 0; i < m_groups.size(); ++i) { Group *category = m_groups.at(i); - category->drawHeader(&painter, option, y); + int y = category->verticalPosition(); + y -= verticalOffset(); + QRect backup = option.rect; + int height = category->totalHeight(); + option.rect.setTop(y); + option.rect.setHeight(height); + option.rect.setLeft(m_leftMargin); + option.rect.setRight(wpWidth - m_rightMargin); + category->drawHeader(&painter, option); y += category->totalHeight() + m_categoryMargin; + option.rect = backup; } for (int i = 0; i < model()->rowCount(); ++i) @@ -587,7 +604,6 @@ void GroupView::startDrag(Qt::DropActions supportedActions) QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); - drag->setHotSpot(m_pressedPosition - rect.topLeft()); Qt::DropAction defaultDropAction = Qt::IgnoreAction; if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) From 583786757a112827ac12efdf6736d1b602e6eb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 8 Feb 2014 23:52:15 +0100 Subject: [PATCH 46/49] Fix crash bug related to messageboxes interrupting model resets in the instance list. --- logic/lists/InstanceList.cpp | 108 +++++++++++++++++++---------------- logic/lists/InstanceList.h | 16 +++++- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 935f9fd3..9a61e2dd 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -47,6 +47,13 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent) QDir::current().mkpath(m_instDir); } + /* + * FIXME HACK: instances sometimes need to be created at launch. They need the versions for + * that. + * + * Remove this. it has no business of reloading the whole list. The instances which + * need it should track such events themselves and CHANGE THEIR DATA ONLY! + */ connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this, &InstanceList::loadList); } @@ -282,16 +289,7 @@ void InstanceList::loadGroupList(QMap &groupMap) } } -struct FTBRecord -{ - QString dir; - QString name; - QString logo; - QString mcVersion; - QString description; -}; - -void InstanceList::loadForgeInstances(QMap groupMap) +QList InstanceList::discoverFTBInstances() { QList records; QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString()); @@ -300,18 +298,18 @@ void InstanceList::loadForgeInstances(QMap groupMap) { QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your " "settings."; - return; + return records; } else if (!dataDir.exists()) { QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings"; - return; + return records; } dir.cd("ModPacks"); auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name); - for(auto filename: allFiles) + for (auto filename : allFiles) { - if(!filename.endsWith(".xml")) + if (!filename.endsWith(".xml")) continue; auto fpath = dir.absoluteFilePath(filename); QFile f(fpath); @@ -331,9 +329,11 @@ void InstanceList::loadForgeInstances(QMap groupMap) { QXmlStreamAttributes attrs = reader.attributes(); FTBRecord record; - record.dir = attrs.value("dir").toString(); - QDir test(dataDir.absoluteFilePath(record.dir)); - if(!test.exists()) + record.dirName = attrs.value("dir").toString(); + record.instanceDir = dataDir.absoluteFilePath(record.dirName); + record.templateDir = dir.absoluteFilePath(record.dirName); + QDir test(record.instanceDir); + if (!test.exists()) continue; record.name = attrs.value("name").toString(); record.logo = attrs.value("logo").toString(); @@ -353,8 +353,14 @@ void InstanceList::loadForgeInstances(QMap groupMap) } f.close(); } + return records; +} - if(!records.size()) +void InstanceList::loadFTBInstances(QMap &groupMap, + QList &tempList) +{ + auto records = discoverFTBInstances(); + if (!records.size()) { QLOG_INFO() << "No FTB instances to load."; return; @@ -363,20 +369,13 @@ void InstanceList::loadForgeInstances(QMap groupMap) // process the records we acquired. for (auto record : records) { - auto instanceDir = dataDir.absoluteFilePath(record.dir); - QLOG_INFO() << "Loading FTB instance from " << instanceDir; - auto templateDir = dir.absoluteFilePath(record.dir); - if (!QFileInfo(instanceDir).exists()) - { - continue; - } - + QLOG_INFO() << "Loading FTB instance from " << record.instanceDir; QString iconKey = record.logo; iconKey.remove(QRegularExpression("\\..*")); - MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo), + MMC->icons()->addIcon(iconKey, iconKey, PathCombine(record.templateDir, record.logo), MMCIcon::Transient); - if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists()) + if (!QFileInfo(PathCombine(record.instanceDir, "instance.cfg")).exists()) { QLOG_INFO() << "Converting " << record.name << " as new."; BaseInstance *instPtr = NULL; @@ -384,12 +383,12 @@ void InstanceList::loadForgeInstances(QMap groupMap) auto version = MMC->minecraftlist()->findVersion(record.mcVersion); if (!version) { - QLOG_ERROR() << "Can't load instance " << instanceDir + QLOG_ERROR() << "Can't load instance " << record.instanceDir << " because minecraft version " << record.mcVersion << " can't be resolved."; continue; } - auto error = factory.createInstance(instPtr, version, instanceDir, + auto error = factory.createInstance(instPtr, version, record.instanceDir, InstanceFactory::FTBInstance); if (!instPtr || error != InstanceFactory::NoCreateError) @@ -400,13 +399,15 @@ void InstanceList::loadForgeInstances(QMap groupMap) instPtr->setIconKey(iconKey); instPtr->setIntendedVersionId(record.mcVersion); instPtr->setNotes(record.description); - continueProcessInstance(instPtr, error, instanceDir, groupMap); + if(!continueProcessInstance(instPtr, error, record.instanceDir, groupMap)) + continue; + tempList.append(InstancePtr(instPtr)); } else { QLOG_INFO() << "Loading existing " << record.name; BaseInstance *instPtr = NULL; - auto error = InstanceFactory::get().loadInstance(instPtr, instanceDir); + auto error = InstanceFactory::get().loadInstance(instPtr, record.instanceDir); if (!instPtr || error != InstanceFactory::NoCreateError) continue; instPtr->setGroupInitial("FTB"); @@ -415,7 +416,9 @@ void InstanceList::loadForgeInstances(QMap groupMap) if (instPtr->intendedVersionId() != record.mcVersion) instPtr->setIntendedVersionId(record.mcVersion); instPtr->setNotes(record.description); - continueProcessInstance(instPtr, error, instanceDir, groupMap); + if(!continueProcessInstance(instPtr, error, record.instanceDir, groupMap)) + continue; + tempList.append(InstancePtr(instPtr)); } } } @@ -426,10 +429,7 @@ InstanceList::InstListError InstanceList::loadList() QMap groupMap; loadGroupList(groupMap); - beginResetModel(); - - m_instances.clear(); - + QList tempList; { QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, QDirIterator::FollowSymlinks); @@ -441,15 +441,28 @@ InstanceList::InstListError InstanceList::loadList() QLOG_INFO() << "Loading MultiMC instance from " << subDir; BaseInstance *instPtr = NULL; auto error = InstanceFactory::get().loadInstance(instPtr, subDir); - continueProcessInstance(instPtr, error, subDir, groupMap); + if(!continueProcessInstance(instPtr, error, subDir, groupMap)) + continue; + tempList.append(InstancePtr(instPtr)); } } if (MMC->settings()->get("TrackFTBInstances").toBool()) { - loadForgeInstances(groupMap); + loadFTBInstances(groupMap, tempList); + } + beginResetModel(); + m_instances.clear(); + for(auto inst: tempList) + { + inst->setParent(this); + connect(inst.get(), SIGNAL(propertiesChanged(BaseInstance *)), this, + SLOT(propertiesChanged(BaseInstance *))); + connect(inst.get(), SIGNAL(groupChanged()), this, SLOT(groupChanged())); + connect(inst.get(), SIGNAL(nuked(BaseInstance *)), this, + SLOT(instanceNuked(BaseInstance *))); + m_instances.append(inst); } - endResetModel(); emit dataIsInvalid(); return NoError; @@ -523,7 +536,7 @@ int InstanceList::getInstIndex(BaseInstance *inst) const return -1; } -void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, +bool InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap) { if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) @@ -539,12 +552,14 @@ void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int erro break; } QLOG_ERROR() << errorMsg.toUtf8(); + return false; } else if (!instPtr) { QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") .arg(QFileInfo(dir.absolutePath()).baseName()) .toUtf8(); + return false; } else { @@ -554,13 +569,7 @@ void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int erro instPtr->setGroupInitial((*iter)); } QLOG_INFO() << "Loaded instance " << instPtr->name() << " from " << dir.absolutePath(); - instPtr->setParent(this); - m_instances.append(std::shared_ptr(instPtr)); - connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this, - SLOT(propertiesChanged(BaseInstance *))); - connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged())); - connect(instPtr, SIGNAL(nuked(BaseInstance *)), this, - SLOT(instanceNuked(BaseInstance *))); + return true; } } @@ -584,8 +593,7 @@ void InstanceList::propertiesChanged(BaseInstance *inst) } } -InstanceProxyModel::InstanceProxyModel(QObject *parent) - : GroupedProxyModel(parent) +InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(parent) { } diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index cda51a03..ebe3e051 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -27,11 +27,24 @@ class BaseInstance; class QDir; +struct FTBRecord +{ + QString dirName; + QString name; + QString logo; + QString mcVersion; + QString description; + QString instanceDir; + QString templateDir; +}; + class InstanceList : public QAbstractListModel { Q_OBJECT private: void loadGroupList(QMap &groupList); + QList discoverFTBInstances(); + void loadFTBInstances(QMap &groupMap, QList & tempList); private slots: @@ -109,7 +122,6 @@ slots: * \brief Loads the instance list. Triggers notifications. */ InstListError loadList(); - void loadForgeInstances(QMap groupMap); private slots: @@ -120,7 +132,7 @@ slots: private: int getInstIndex(BaseInstance *inst) const; - void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, + bool continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap); protected: From 902204236031dedffc6047802524c93dcc14fa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 9 Feb 2014 11:00:34 +0100 Subject: [PATCH 47/49] Only load instance list twice. --- logic/lists/MinecraftVersionList.cpp | 10 +++++++--- logic/lists/MinecraftVersionList.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 91f86df0..ece31e3d 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -60,10 +60,15 @@ bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) return left->timestamp > right->timestamp; } +void MinecraftVersionList::sortInternal() +{ + qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); +} + void MinecraftVersionList::sort() { beginResetModel(); - qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); + sortInternal(); endResetModel(); } @@ -85,9 +90,8 @@ void MinecraftVersionList::updateListData(QList versions) beginResetModel(); m_vlist = versions; m_loaded = true; + sortInternal(); endResetModel(); - // NOW SORT!! - sort(); } inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname) diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h index 82af1009..167f4d11 100644 --- a/logic/lists/MinecraftVersionList.h +++ b/logic/lists/MinecraftVersionList.h @@ -29,6 +29,8 @@ class QNetworkReply; class MinecraftVersionList : public BaseVersionList { Q_OBJECT +private: + void sortInternal(); public: friend class MCVListLoadTask; From 0d30a2655ffb767488d50a4a79978b3d9ddb3aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 9 Feb 2014 19:10:56 +0100 Subject: [PATCH 48/49] Blacklist the FTB voxel pack. --- logic/lists/InstanceList.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 9a61e2dd..cd59e6d6 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -336,6 +336,8 @@ QList InstanceList::discoverFTBInstances() if (!test.exists()) continue; record.name = attrs.value("name").toString(); + if(record.name.contains("voxel", Qt::CaseInsensitive)) + continue; record.logo = attrs.value("logo").toString(); record.mcVersion = attrs.value("mcVersion").toString(); record.description = attrs.value("description").toString(); From 18f532b0d7d873280ec17218196db15fa64175a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 9 Feb 2014 20:45:18 +0100 Subject: [PATCH 49/49] Visual and scroll behavior changes to groupview Scroll by rows, not pixels. Paint the checkboxy thing again! Make0 it behave. Set the group header height properly. --- gui/groupview/Group.cpp | 257 ++++++++++++++++++------------------ gui/groupview/GroupView.cpp | 6 + 2 files changed, 134 insertions(+), 129 deletions(-) diff --git a/gui/groupview/Group.cpp b/gui/groupview/Group.cpp index f3319dc3..51aa6658 100644 --- a/gui/groupview/Group.cpp +++ b/gui/groupview/Group.cpp @@ -61,143 +61,133 @@ Group::HitResults Group::hitScan(const QPoint &pos) const void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) { - /* - QStyleOptionViewItemV4 opt = option; - painter->save(); - - static const int margin = 2; - static const int spacing = 10; - int height = headerHeight(); - int text_height = height - 2 * margin; - - // set the text colors - QPalette::ColorGroup cg = QPalette::Normal; - painter->setPen(opt.palette.color(cg, QPalette::Text)); - - // set up geometry - QRect iconRect = QRect(view->m_leftMargin + margin, y + margin, text_height - 1, text_height - 1); - QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); - QRect smallRect = iconSubrect.adjusted(margin, margin, -margin, -margin); - int midX = iconSubrect.center().x(); - int midY = iconSubrect.center().y(); - - // checkboxy thingy - { - painter->drawRect(iconSubrect); - - painter->setBrush(opt.palette.text()); - painter->drawRect(smallRect.x(), midY, smallRect.width(), 2); - if(collapsed) - { - painter->drawRect(midX, smallRect.y(), 2, smallRect.height()); - } - } - - int x_left = iconRect.right(); - - if(text.length()) - { - // the text - int text_width = painter->fontMetrics().width(text); - QRect textRect = QRect(x_left + spacing, y + margin, text_width, text_height); - x_left = textRect.right(); - view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, - opt.palette, true, text); - } - // the line - painter->drawLine(x_left + spacing, midY + 1, view->contentWidth() - view->m_rightMargin, - midY + 1); - - painter->restore(); - */ painter->setRenderHint(QPainter::Antialiasing); - const QRect optRect = option.rect; - QFont font(QApplication::font()); - font.setBold(true); - const QFontMetrics fontMetrics = QFontMetrics(font); + const QRect optRect = option.rect; + QFont font(QApplication::font()); + font.setBold(true); + const QFontMetrics fontMetrics = QFontMetrics(font); - QColor outlineColor = option.palette.text().color(); - outlineColor.setAlphaF(0.35); + QColor outlineColor = option.palette.text().color(); + outlineColor.setAlphaF(0.35); - //BEGIN: top left corner - { - painter->save(); - painter->setPen(outlineColor); - const QPointF topLeft(optRect.topLeft()); - QRectF arc(topLeft, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 1440, 1440); - painter->restore(); - } - //END: top left corner + //BEGIN: top left corner + { + painter->save(); + painter->setPen(outlineColor); + const QPointF topLeft(optRect.topLeft()); + QRectF arc(topLeft, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 1440, 1440); + painter->restore(); + } + //END: top left corner - //BEGIN: left vertical line - { - QPoint start(optRect.topLeft()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topLeft()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: left vertical line + //BEGIN: left vertical line + { + QPoint start(optRect.topLeft()); + start.ry() += 3; + QPoint verticalGradBottom(optRect.topLeft()); + verticalGradBottom.ry() += fontMetrics.height() + 5; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); + } + //END: left vertical line - //BEGIN: horizontal line - { - QPoint start(optRect.topLeft()); - start.rx() += 3; - QPoint horizontalGradTop(optRect.topLeft()); - horizontalGradTop.rx() += optRect.width() - 6; - painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); - } - //END: horizontal line + //BEGIN: horizontal line + { + QPoint start(optRect.topLeft()); + start.rx() += 3; + QPoint horizontalGradTop(optRect.topLeft()); + horizontalGradTop.rx() += optRect.width() - 6; + painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); + } + //END: horizontal line - //BEGIN: top right corner - { - painter->save(); - painter->setPen(outlineColor); - QPointF topRight(optRect.topRight()); - topRight.rx() -= 4; - QRectF arc(topRight, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 0, 1440); - painter->restore(); - } - //END: top right corner + //BEGIN: top right corner + { + painter->save(); + painter->setPen(outlineColor); + QPointF topRight(optRect.topRight()); + topRight.rx() -= 4; + QRectF arc(topRight, QSizeF(4, 4)); + arc.translate(0.5, 0.5); + painter->drawArc(arc, 0, 1440); + painter->restore(); + } + //END: top right corner - //BEGIN: right vertical line - { - QPoint start(optRect.topRight()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topRight()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: right vertical line + //BEGIN: right vertical line + { + QPoint start(optRect.topRight()); + start.ry() += 3; + QPoint verticalGradBottom(optRect.topRight()); + verticalGradBottom.ry() += fontMetrics.height() + 5; + QLinearGradient gradient(start, verticalGradBottom); + gradient.setColorAt(0, outlineColor); + gradient.setColorAt(1, Qt::transparent); + painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); + } + //END: right vertical line - //BEGIN: text - { - QRect textRect(option.rect); - textRect.setTop(textRect.top() + 7); - textRect.setLeft(textRect.left() + 7); - textRect.setHeight(fontMetrics.height()); - textRect.setRight(textRect.right() - 7); + //BEGIN: checkboxy thing + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, false); + painter->setFont(font); + QColor penColor(option.palette.text().color()); + penColor.setAlphaF(0.6); + painter->setPen(penColor); + QRect iconSubRect(option.rect); + iconSubRect.setTop(iconSubRect.top() + 7); + iconSubRect.setLeft(iconSubRect.left() + 7); - painter->save(); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); - painter->restore(); - } - //END: text + int sizing = fontMetrics.height(); + int even = ( (sizing - 1) % 2 ); + + iconSubRect.setHeight(sizing - even); + iconSubRect.setWidth(sizing - even); + painter->drawRect(iconSubRect); + + + /* + if(collapsed) + painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "+"); + else + painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "-"); + */ + painter->setBrush(option.palette.text()); + painter->fillRect(iconSubRect.x(), iconSubRect.y() + iconSubRect.height() / 2, + iconSubRect.width(), 2, penColor); + if (collapsed) + { + painter->fillRect(iconSubRect.x() + iconSubRect.width() / 2, iconSubRect.y(), 2, + iconSubRect.height(), penColor); + } + + painter->restore(); + } + //END: checkboxy thing + + //BEGIN: text + { + QRect textRect(option.rect); + textRect.setTop(textRect.top() + 7); + textRect.setLeft(textRect.left() + 7 + fontMetrics.height() + 7); + textRect.setHeight(fontMetrics.height()); + textRect.setRight(textRect.right() - 7); + + painter->save(); + painter->setFont(font); + QColor penColor(option.palette.text().color()); + penColor.setAlphaF(0.6); + painter->setPen(penColor); + painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); + painter->restore(); + } + //END: text } int Group::totalHeight() const @@ -207,11 +197,20 @@ int Group::totalHeight() const int Group::headerHeight() const { + QFont font(QApplication::font()); + font.setBold(true); + QFontMetrics fontMetrics(font); + + const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */ + + 11 /* top and bottom separation */; + return height; + /* int raw = view->viewport()->fontMetrics().height() + 4; // add english. maybe. depends on font height. - if(raw % 2 == 0) + if (raw % 2 == 0) raw++; - return std::min( raw , 25 ); + return std::min(raw, 25); + */ } int Group::contentHeight() const diff --git a/gui/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp index 9954d743..5ee44cbb 100644 --- a/gui/groupview/GroupView.cpp +++ b/gui/groupview/GroupView.cpp @@ -121,6 +121,8 @@ void GroupView::updateGeometries() category->m_verticalPosition = totalHeight; totalHeight += category->totalHeight() + m_categoryMargin; } + auto category = m_groups.last(); + int itemScroll = category->contentHeight() / category->numRows(); /* // remove the last margin (we don't want it) totalHeight -= m_categoryMargin; @@ -128,6 +130,10 @@ void GroupView::updateGeometries() totalHeight += m_categoryMargin; */ totalHeight += m_bottomMargin; + verticalScrollBar()->setSingleStep ( itemScroll ); + const int rowsPerPage = qMax ( viewport()->height() / itemScroll, 1 ); + verticalScrollBar()->setPageStep ( rowsPerPage * itemScroll ); + verticalScrollBar()->setRange(0, totalHeight - height()); }