summaryrefslogtreecommitdiffstats
path: root/application/widgets
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2015-02-09 01:51:14 +0100
committerPetr Mrázek <peterix@gmail.com>2015-04-12 20:57:18 +0200
commitdb877ba121ff87a4e029daf8555d85dfef45993a (patch)
tree7673f83c404b3883f0a4fcf6b492f0c4125c293c /application/widgets
parent4730f54df7edf4775dfddf45f77c60edd86c32d9 (diff)
downloadMultiMC-db877ba121ff87a4e029daf8555d85dfef45993a.tar
MultiMC-db877ba121ff87a4e029daf8555d85dfef45993a.tar.gz
MultiMC-db877ba121ff87a4e029daf8555d85dfef45993a.tar.lz
MultiMC-db877ba121ff87a4e029daf8555d85dfef45993a.tar.xz
MultiMC-db877ba121ff87a4e029daf8555d85dfef45993a.zip
NOISSUE move everything.
Diffstat (limited to 'application/widgets')
-rw-r--r--application/widgets/Common.cpp27
-rw-r--r--application/widgets/Common.h6
-rw-r--r--application/widgets/IconLabel.cpp43
-rw-r--r--application/widgets/IconLabel.h26
-rw-r--r--application/widgets/LabeledToolButton.cpp86
-rw-r--r--application/widgets/LabeledToolButton.h37
-rw-r--r--application/widgets/LineSeparator.cpp37
-rw-r--r--application/widgets/LineSeparator.h18
-rw-r--r--application/widgets/MCModInfoFrame.cpp111
-rw-r--r--application/widgets/MCModInfoFrame.h46
-rw-r--r--application/widgets/MCModInfoFrame.ui68
-rw-r--r--application/widgets/ModListView.cpp62
-rw-r--r--application/widgets/ModListView.h27
-rw-r--r--application/widgets/PageContainer.cpp210
-rw-r--r--application/widgets/PageContainer.h63
-rw-r--r--application/widgets/PageContainer_p.h123
-rw-r--r--application/widgets/ServerStatus.cpp129
-rw-r--r--application/widgets/ServerStatus.h37
-rw-r--r--application/widgets/VersionListView.cpp147
-rw-r--r--application/widgets/VersionListView.h43
20 files changed, 1346 insertions, 0 deletions
diff --git a/application/widgets/Common.cpp b/application/widgets/Common.cpp
new file mode 100644
index 00000000..9b730d6c
--- /dev/null
+++ b/application/widgets/Common.cpp
@@ -0,0 +1,27 @@
+#include "Common.h"
+
+// Origin: Qt
+QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed)
+{
+ QStringList lines;
+ 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();
+ lines.append(str.mid(line.textStart(), line.textLength()));
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+ return lines;
+}
diff --git a/application/widgets/Common.h b/application/widgets/Common.h
new file mode 100644
index 00000000..fc46e08f
--- /dev/null
+++ b/application/widgets/Common.h
@@ -0,0 +1,6 @@
+#pragma once
+#include <QStringList>
+#include <QTextLayout>
+
+QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed); \ No newline at end of file
diff --git a/application/widgets/IconLabel.cpp b/application/widgets/IconLabel.cpp
new file mode 100644
index 00000000..86c8a431
--- /dev/null
+++ b/application/widgets/IconLabel.cpp
@@ -0,0 +1,43 @@
+#include "IconLabel.h"
+
+#include <QStyle>
+#include <QStyleOption>
+#include <QLayout>
+#include <QPainter>
+#include <QRect>
+
+IconLabel::IconLabel(QWidget *parent, QIcon icon, QSize size)
+ : QWidget(parent), m_size(size), m_icon(icon)
+{
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+}
+
+QSize IconLabel::sizeHint() const
+{
+ return m_size;
+}
+
+void IconLabel::setIcon(QIcon icon)
+{
+ m_icon = icon;
+ update();
+}
+
+void IconLabel::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QRect rect = contentsRect();
+ int width = rect.width();
+ int height = rect.height();
+ if(width < height)
+ {
+ rect.setHeight(width);
+ rect.translate(0, (height - width) / 2);
+ }
+ else if (width > height)
+ {
+ rect.setWidth(height);
+ rect.translate((width - height) / 2, 0);
+ }
+ m_icon.paint(&p, rect);
+}
diff --git a/application/widgets/IconLabel.h b/application/widgets/IconLabel.h
new file mode 100644
index 00000000..a2f1eef3
--- /dev/null
+++ b/application/widgets/IconLabel.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <QWidget>
+#include <QIcon>
+
+class QStyleOption;
+
+/**
+ * This is a trivial widget that paints a QIcon of the specified size.
+ */
+class IconLabel : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /// Create a line separator. orientation is the orientation of the line.
+ explicit IconLabel(QWidget *parent, QIcon icon, QSize size);
+
+ virtual QSize sizeHint() const;
+ virtual void paintEvent(QPaintEvent *);
+
+ void setIcon(QIcon icon);
+
+private:
+ QSize m_size;
+ QIcon m_icon;
+};
diff --git a/application/widgets/LabeledToolButton.cpp b/application/widgets/LabeledToolButton.cpp
new file mode 100644
index 00000000..dfdde1bc
--- /dev/null
+++ b/application/widgets/LabeledToolButton.cpp
@@ -0,0 +1,86 @@
+/* Copyright 2013-2015 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 <QLabel>
+#include <QVBoxLayout>
+#include <QResizeEvent>
+#include <QStyleOption>
+#include "LabeledToolButton.h"
+#include <QApplication>
+
+/*
+ *
+ * Tool Button with a label on it, instead of the normal text rendering
+ *
+ */
+
+LabeledToolButton::LabeledToolButton(QWidget * parent)
+ : QToolButton(parent)
+ , m_label(new QLabel(this))
+{
+ //QToolButton::setText(" ");
+ m_label->setWordWrap(true);
+ m_label->setMouseTracking(false);
+ m_label->setAlignment(Qt::AlignCenter);
+ m_label->setTextInteractionFlags(Qt::NoTextInteraction);
+ // somehow, this makes word wrap work in the QLabel. yay.
+ m_label->setMinimumWidth(100);
+}
+
+QString LabeledToolButton::text() const
+{
+ return m_label->text();
+}
+
+void LabeledToolButton::setText(const QString & text)
+{
+ m_label->setText(text);
+}
+
+/*!
+ \reimp
+*/
+QSize LabeledToolButton::sizeHint() const
+{
+ /*
+ Q_D(const QToolButton);
+ if (d->sizeHint.isValid())
+ return d->sizeHint;
+ */
+ ensurePolished();
+
+ int w = 0, h = 0;
+ QStyleOptionToolButton opt;
+ initStyleOption(&opt);
+ QSize sz =m_label->sizeHint();
+ w = sz.width();
+ h = sz.height();
+
+ opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
+ if (popupMode() == MenuButtonPopup)
+ w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
+
+ QSize rawSize = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this);
+ QSize sizeHint = rawSize.expandedTo(QApplication::globalStrut());
+ return sizeHint;
+}
+
+
+
+void LabeledToolButton::resizeEvent(QResizeEvent * event)
+{
+ m_label->setGeometry(QRect(4, 4, width()-8, height()-8));
+ QWidget::resizeEvent(event);
+}
diff --git a/application/widgets/LabeledToolButton.h b/application/widgets/LabeledToolButton.h
new file mode 100644
index 00000000..1b74c4ee
--- /dev/null
+++ b/application/widgets/LabeledToolButton.h
@@ -0,0 +1,37 @@
+/* Copyright 2013-2015 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 <QPushButton>
+#include <QToolButton>
+
+class QLabel;
+
+class LabeledToolButton : public QToolButton
+{
+ Q_OBJECT
+
+ QLabel * m_label;
+
+public:
+ LabeledToolButton(QWidget * parent = 0);
+
+ QString text() const;
+ void setText(const QString & text);
+ virtual QSize sizeHint() const;
+protected:
+ void resizeEvent(QResizeEvent * event);
+};
diff --git a/application/widgets/LineSeparator.cpp b/application/widgets/LineSeparator.cpp
new file mode 100644
index 00000000..f4ee173d
--- /dev/null
+++ b/application/widgets/LineSeparator.cpp
@@ -0,0 +1,37 @@
+#include "LineSeparator.h"
+
+#include <QStyle>
+#include <QStyleOption>
+#include <QLayout>
+#include <QPainter>
+
+void LineSeparator::initStyleOption(QStyleOption *option) const
+{
+ option->initFrom(this);
+ // in a horizontal layout, the line is vertical (and vice versa)
+ if (m_orientation == Qt::Vertical)
+ option->state |= QStyle::State_Horizontal;
+}
+
+LineSeparator::LineSeparator(QWidget *parent, Qt::Orientation orientation)
+ : QWidget(parent), m_orientation(orientation)
+{
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+}
+
+QSize LineSeparator::sizeHint() const
+{
+ QStyleOption opt;
+ initStyleOption(&opt);
+ const int extent =
+ style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, parentWidget());
+ return QSize(extent, extent);
+}
+
+void LineSeparator::paintEvent(QPaintEvent *)
+{
+ QPainter p(this);
+ QStyleOption opt;
+ initStyleOption(&opt);
+ style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p, parentWidget());
+}
diff --git a/application/widgets/LineSeparator.h b/application/widgets/LineSeparator.h
new file mode 100644
index 00000000..9546e747
--- /dev/null
+++ b/application/widgets/LineSeparator.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <QWidget>
+
+class QStyleOption;
+
+class LineSeparator : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /// Create a line separator. orientation is the orientation of the line.
+ explicit LineSeparator(QWidget *parent, Qt::Orientation orientation = Qt::Horizontal);
+ QSize sizeHint() const;
+ void paintEvent(QPaintEvent *);
+ void initStyleOption(QStyleOption *option) const;
+private:
+ Qt::Orientation m_orientation = Qt::Horizontal;
+};
diff --git a/application/widgets/MCModInfoFrame.cpp b/application/widgets/MCModInfoFrame.cpp
new file mode 100644
index 00000000..2f859ed3
--- /dev/null
+++ b/application/widgets/MCModInfoFrame.cpp
@@ -0,0 +1,111 @@
+/* Copyright 2013-2015 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 <QMessageBox>
+#include <QtGui>
+
+#include "MCModInfoFrame.h"
+#include "ui_MCModInfoFrame.h"
+#include "dialogs/CustomMessageBox.h"
+
+void MCModInfoFrame::updateWithMod(Mod &m)
+{
+ if(m.type() == m.MOD_FOLDER)
+ {
+ clear();
+ return;
+ }
+
+ QString text = "";
+ QString name = "";
+ if(m.name().isEmpty()) name = m.mmc_id();
+ else name = m.name();
+
+ if(m.homeurl().isEmpty()) text = name;
+ else text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>";
+ if(!m.authors().isEmpty()) text += " by " + m.authors();
+
+ setModText(text);
+
+ if(m.description().isEmpty())
+ {
+ setModDescription(tr("No description provided in mcmod.info"));
+ }
+ else
+ {
+ setModDescription(m.description());
+ }
+}
+
+void MCModInfoFrame::clear()
+{
+ setModText(tr("Select a mod to view title and authors..."));
+ setModDescription(tr("Select a mod to view description..."));
+}
+
+MCModInfoFrame::MCModInfoFrame(QWidget *parent) :
+ QFrame(parent),
+ ui(new Ui::MCModInfoFrame)
+{
+ ui->setupUi(this);
+}
+
+MCModInfoFrame::~MCModInfoFrame()
+{
+ delete ui;
+}
+
+void MCModInfoFrame::setModText(QString text)
+{
+ ui->label_ModText->setText(text);
+}
+
+void MCModInfoFrame::setModDescription(QString text)
+{
+ ui->label_ModDescription->setToolTip("");
+ QString intermediatetext = text.trimmed();
+ bool prev(false);
+ QChar rem('\n');
+ QString finaltext;
+ finaltext.reserve(intermediatetext.size());
+ foreach(const QChar& c, intermediatetext)
+ {
+ if(c == rem && prev){
+ continue;
+ }
+ prev = c == rem;
+ finaltext += c;
+ }
+ QString labeltext;
+ labeltext.reserve(300);
+ if(finaltext.length() > 290)
+ {
+ ui->label_ModDescription->setOpenExternalLinks(false);
+ ui->label_ModDescription->setTextFormat(Qt::TextFormat::RichText);
+ desc = text;
+ labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
+ QObject::connect(ui->label_ModDescription, &QLabel::linkActivated, this, &MCModInfoFrame::modDescEllipsisHandler);
+ }
+ else
+ {
+ ui->label_ModDescription->setTextFormat(Qt::TextFormat::PlainText);
+ labeltext.append(finaltext);
+ }
+ ui->label_ModDescription->setText(labeltext);
+}
+void MCModInfoFrame::modDescEllipsisHandler(const QString &link)
+{
+ CustomMessageBox::selectable(this, tr(""), desc)->show();
+}
diff --git a/application/widgets/MCModInfoFrame.h b/application/widgets/MCModInfoFrame.h
new file mode 100644
index 00000000..3f75279c
--- /dev/null
+++ b/application/widgets/MCModInfoFrame.h
@@ -0,0 +1,46 @@
+/* Copyright 2013-2015 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 <QFrame>
+#include "minecraft/Mod.h"
+
+namespace Ui
+{
+class MCModInfoFrame;
+}
+
+class MCModInfoFrame : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit MCModInfoFrame(QWidget *parent = 0);
+ ~MCModInfoFrame();
+
+ void setModText(QString text);
+ void setModDescription(QString text);
+
+ void updateWithMod(Mod &m);
+ void clear();
+
+public slots:
+ void modDescEllipsisHandler(const QString& link );
+
+private:
+ Ui::MCModInfoFrame *ui;
+ QString desc;
+};
diff --git a/application/widgets/MCModInfoFrame.ui b/application/widgets/MCModInfoFrame.ui
new file mode 100644
index 00000000..60e0a65c
--- /dev/null
+++ b/application/widgets/MCModInfoFrame.ui
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MCModInfoFrame</class>
+ <widget class="QFrame" name="MCModInfoFrame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>527</width>
+ <height>113</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_ModText">
+ <property name="text">
+ <string>Select a mod to view title and authors...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_ModDescription">
+ <property name="text">
+ <string>Select a mod to view description...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/application/widgets/ModListView.cpp b/application/widgets/ModListView.cpp
new file mode 100644
index 00000000..aa16ad05
--- /dev/null
+++ b/application/widgets/ModListView.cpp
@@ -0,0 +1,62 @@
+/* Copyright 2013-2015 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 "ModListView.h"
+#include <QHeaderView>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QDrag>
+#include <QRect>
+
+ModListView::ModListView ( QWidget* parent )
+ :QTreeView ( parent )
+{
+ setAllColumnsShowFocus ( true );
+ setExpandsOnDoubleClick ( false );
+ setRootIsDecorated ( false );
+ setSortingEnabled ( false );
+ setAlternatingRowColors ( true );
+ setSelectionMode ( QAbstractItemView::ContiguousSelection );
+ setHeaderHidden ( false );
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn );
+ setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
+ setDropIndicatorShown(true);
+ setDragEnabled(true);
+ setDragDropMode(QAbstractItemView::DropOnly);
+ viewport()->setAcceptDrops(true);
+}
+
+void ModListView::setModel ( QAbstractItemModel* model )
+{
+ QTreeView::setModel ( model );
+ auto head = header();
+ head->setStretchLastSection(false);
+ // HACK: this is true for the checkbox column of mod lists
+ auto string = model->headerData(0,head->orientation()).toString();
+ if(!string.size())
+ {
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+ else
+ {
+ head->setSectionResizeMode(0, QHeaderView::Stretch);
+ for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+}
diff --git a/application/widgets/ModListView.h b/application/widgets/ModListView.h
new file mode 100644
index 00000000..43de9811
--- /dev/null
+++ b/application/widgets/ModListView.h
@@ -0,0 +1,27 @@
+/* Copyright 2013-2015 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 <QTreeView>
+
+class Mod;
+
+class ModListView: public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit ModListView ( QWidget* parent = 0 );
+ virtual void setModel ( QAbstractItemModel* model );
+};
diff --git a/application/widgets/PageContainer.cpp b/application/widgets/PageContainer.cpp
new file mode 100644
index 00000000..679039a7
--- /dev/null
+++ b/application/widgets/PageContainer.cpp
@@ -0,0 +1,210 @@
+/* Copyright 2013-2015 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 "PageContainer.h"
+
+#include <QStackedLayout>
+#include <QPushButton>
+#include <QSortFilterProxyModel>
+#include <QUrl>
+#include <QStyledItemDelegate>
+#include <QListView>
+#include <QLineEdit>
+#include <QLabel>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QDesktopServices>
+
+#include "MultiMC.h"
+#include "settings/SettingsObject.h"
+#include "widgets/IconLabel.h"
+#include "Platform.h"
+#include "PageContainer_p.h"
+#include <MultiMC.h>
+
+class PageEntryFilterModel : public QSortFilterProxyModel
+{
+public:
+ explicit PageEntryFilterModel(QObject *parent = 0) : QSortFilterProxyModel(parent)
+ {
+ }
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+ {
+ const QString pattern = filterRegExp().pattern();
+ const auto model = static_cast<PageModel *>(sourceModel());
+ const auto page = model->pages().at(sourceRow);
+ if (!page->shouldDisplay())
+ return false;
+ // Regular contents check, then check page-filter.
+ return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
+ }
+};
+
+PageContainer::PageContainer(BasePageProviderPtr pageProvider, QString defaultId,
+ QWidget *parent)
+ : QWidget(parent)
+{
+ createUI();
+ m_model = new PageModel(this);
+ m_proxyModel = new PageEntryFilterModel(this);
+ int firstIndex = -1;
+ int counter = 0;
+ auto pages = pageProvider->getPages();
+ for (auto page : pages)
+ {
+ page->stackIndex = m_pageStack->addWidget(dynamic_cast<QWidget *>(page));
+ page->listIndex = counter;
+ counter++;
+ if (firstIndex == -1)
+ {
+ firstIndex = page->stackIndex;
+ }
+ }
+ m_model->setPages(pages);
+
+ m_proxyModel->setSourceModel(m_model);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+ m_pageList->setIconSize(QSize(pageIconSize, pageIconSize));
+ m_pageList->setSelectionMode(QAbstractItemView::SingleSelection);
+ m_pageList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ m_pageList->setModel(m_proxyModel);
+ connect(m_pageList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
+ this, SLOT(currentChanged(QModelIndex)));
+ m_pageStack->setStackingMode(QStackedLayout::StackOne);
+ m_pageList->setFocus();
+ // now find what we want to have selected...
+ auto page = m_model->findPageEntryById(defaultId);
+ QModelIndex index;
+ if (page)
+ {
+ index = m_proxyModel->mapFromSource(m_model->index(page->listIndex));
+ }
+ else
+ {
+ index = m_proxyModel->index(0, 0);
+ }
+ if (index.isValid())
+ m_pageList->setCurrentIndex(index);
+}
+
+void PageContainer::createUI()
+{
+ m_pageStack = new QStackedLayout;
+ m_filter = new QLineEdit;
+ m_pageList = new PageView;
+ m_header = new QLabel();
+ m_iconHeader = new IconLabel(this, QIcon(), QSize(24, 24));
+
+ QFont headerLabelFont = m_header->font();
+ headerLabelFont.setBold(true);
+ const int pointSize = headerLabelFont.pointSize();
+ if (pointSize > 0)
+ headerLabelFont.setPointSize(pointSize + 2);
+ m_header->setFont(headerLabelFont);
+
+ QHBoxLayout *headerHLayout = new QHBoxLayout;
+ const int leftMargin = MMC->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+ headerHLayout->addSpacerItem(
+ new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
+ headerHLayout->addWidget(m_header);
+ headerHLayout->addSpacerItem(
+ new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
+ headerHLayout->addWidget(m_iconHeader);
+ const int rightMargin = MMC->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+ headerHLayout->addSpacerItem(
+ new QSpacerItem(rightMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
+
+ m_pageStack->setMargin(0);
+ m_pageStack->addWidget(new QWidget(this));
+
+ m_layout = new QGridLayout;
+ m_layout->addLayout(headerHLayout, 0, 1, 1, 1);
+ m_layout->addWidget(m_pageList, 0, 0, 2, 1);
+ m_layout->addLayout(m_pageStack, 1, 1, 1, 1);
+ m_layout->setColumnStretch(1, 4);
+ setLayout(m_layout);
+}
+
+void PageContainer::addButtons(QWidget *buttons)
+{
+ m_layout->addWidget(buttons, 2, 0, 1, 2);
+}
+
+void PageContainer::addButtons(QLayout *buttons)
+{
+ m_layout->addLayout(buttons, 2, 0, 1, 2);
+}
+
+void PageContainer::showPage(int row)
+{
+ if (m_currentPage)
+ {
+ m_currentPage->closed();
+ }
+ if (row != -1)
+ {
+ m_currentPage = m_model->pages().at(row);
+ }
+ else
+ {
+ m_currentPage = nullptr;
+ }
+ if (m_currentPage)
+ {
+ m_pageStack->setCurrentIndex(m_currentPage->stackIndex);
+ m_header->setText(m_currentPage->displayName());
+ m_iconHeader->setIcon(m_currentPage->icon());
+ m_currentPage->opened();
+ }
+ else
+ {
+ m_pageStack->setCurrentIndex(0);
+ m_header->setText(QString());
+ m_iconHeader->setIcon(MMC->getThemedIcon("bug"));
+ }
+}
+
+void PageContainer::help()
+{
+ if (m_currentPage)
+ {
+ QString pageId = m_currentPage->helpPage();
+ if (pageId.isEmpty())
+ return;
+ QDesktopServices::openUrl(QUrl("https://github.com/MultiMC/MultiMC5/wiki/" + pageId));
+ }
+}
+
+void PageContainer::currentChanged(const QModelIndex &current)
+{
+ showPage(current.isValid() ? m_proxyModel->mapToSource(current).row() : -1);
+}
+
+bool PageContainer::requestClose(QCloseEvent *event)
+{
+ for (auto page : m_model->pages())
+ {
+ if (!page->apply())
+ return false;
+ }
+ if (m_currentPage)
+ {
+ m_currentPage->closed();
+ }
+ return true;
+}
diff --git a/application/widgets/PageContainer.h b/application/widgets/PageContainer.h
new file mode 100644
index 00000000..3027af36
--- /dev/null
+++ b/application/widgets/PageContainer.h
@@ -0,0 +1,63 @@
+/* Copyright 2013-2015 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 <QWidget>
+#include <QModelIndex>
+
+#include "pages/BasePageProvider.h"
+
+class QLayout;
+class IconLabel;
+class QSortFilterProxyModel;
+class PageModel;
+class QLabel;
+class QListView;
+class QLineEdit;
+class QStackedLayout;
+class QGridLayout;
+
+class PageContainer : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit PageContainer(BasePageProviderPtr pageProvider, QString defaultId = QString(),
+ QWidget *parent = 0);
+ virtual ~PageContainer() {}
+
+ void addButtons(QWidget * buttons);
+ void addButtons(QLayout * buttons);
+ bool requestClose(QCloseEvent *event);
+
+private:
+ void createUI();
+private
+slots:
+ void currentChanged(const QModelIndex &current);
+ void showPage(int row);
+ void help();
+
+private:
+ BasePage * m_currentPage = 0;
+ QSortFilterProxyModel *m_proxyModel;
+ PageModel *m_model;
+ QStackedLayout *m_pageStack;
+ QLineEdit *m_filter;
+ QListView *m_pageList;
+ QLabel *m_header;
+ IconLabel *m_iconHeader;
+ QGridLayout *m_layout;
+};
diff --git a/application/widgets/PageContainer_p.h b/application/widgets/PageContainer_p.h
new file mode 100644
index 00000000..4c720e4c
--- /dev/null
+++ b/application/widgets/PageContainer_p.h
@@ -0,0 +1,123 @@
+/* Copyright 2013-2015 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 <QListView>
+#include <QStyledItemDelegate>
+#include <QEvent>
+#include <QScrollBar>
+
+class BasePage;
+const int pageIconSize = 24;
+
+class PageViewDelegate : public QStyledItemDelegate
+{
+public:
+ PageViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
+ {
+ }
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
+ {
+ QSize size = QStyledItemDelegate::sizeHint(option, index);
+ size.setHeight(qMax(size.height(), 32));
+ return size;
+ }
+};
+
+class PageModel : public QAbstractListModel
+{
+public:
+ PageModel(QObject *parent = 0) : QAbstractListModel(parent)
+ {
+ QPixmap empty(pageIconSize, pageIconSize);
+ empty.fill(Qt::transparent);
+ m_emptyIcon = QIcon(empty);
+ }
+ virtual ~PageModel() {}
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const
+ {
+ return parent.isValid() ? 0 : m_pages.size();
+ }
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
+ {
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return m_pages.at(index.row())->displayName();
+ case Qt::DecorationRole:
+ {
+ QIcon icon = m_pages.at(index.row())->icon();
+ if (icon.isNull())
+ icon = m_emptyIcon;
+ // HACK: fixes icon stretching on windows. TODO: report Qt bug for this
+ return QIcon(icon.pixmap(QSize(48,48)));
+ }
+ }
+ return QVariant();
+ }
+
+ void setPages(const QList<BasePage *> &pages)
+ {
+ beginResetModel();
+ m_pages = pages;
+ endResetModel();
+ }
+ const QList<BasePage *> &pages() const
+ {
+ return m_pages;
+ }
+
+ BasePage * findPageEntryById(QString id)
+ {
+ for(auto page: m_pages)
+ {
+ if (page->id() == id)
+ return page;
+ }
+ return nullptr;
+ }
+
+ QList<BasePage *> m_pages;
+ QIcon m_emptyIcon;
+};
+
+class PageView : public QListView
+{
+public:
+ PageView(QWidget *parent = 0) : QListView(parent)
+ {
+ setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
+ setItemDelegate(new PageViewDelegate(this));
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ }
+
+ virtual QSize sizeHint() const
+ {
+ int width = sizeHintForColumn(0) + frameWidth() * 2 + 5;
+ if (verticalScrollBar()->isVisible())
+ width += verticalScrollBar()->width();
+ return QSize(width, 100);
+ }
+
+ virtual bool eventFilter(QObject *obj, QEvent *event)
+ {
+ if (obj == verticalScrollBar() &&
+ (event->type() == QEvent::Show || event->type() == QEvent::Hide))
+ updateGeometry();
+ return QListView::eventFilter(obj, event);
+ }
+};
diff --git a/application/widgets/ServerStatus.cpp b/application/widgets/ServerStatus.cpp
new file mode 100644
index 00000000..0c11b9bf
--- /dev/null
+++ b/application/widgets/ServerStatus.cpp
@@ -0,0 +1,129 @@
+#include "ServerStatus.h"
+#include "LineSeparator.h"
+#include "IconLabel.h"
+#include "status/StatusChecker.h"
+
+#include "MultiMC.h"
+
+#include <QHBoxLayout>
+#include <QFrame>
+#include <QLabel>
+#include <QMap>
+#include <QToolButton>
+#include <QAction>
+
+ServerStatus::ServerStatus(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f)
+{
+ layout = new QHBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ goodIcon = MMC->getThemedIcon("status-good");
+ yellowIcon = MMC->getThemedIcon("status-yellow");
+ badIcon = MMC->getThemedIcon("status-bad");
+
+ addStatus("minecraft.net", tr("Web"));
+ addLine();
+ addStatus("account.mojang.com", tr("Account"));
+ addLine();
+ addStatus("skins.minecraft.net", tr("Skins"));
+ addLine();
+ addStatus("authserver.mojang.com", tr("Auth"));
+ addLine();
+ addStatus("sessionserver.mojang.com", tr("Session"));
+
+ m_statusRefresh = new QToolButton(this);
+ m_statusRefresh->setCheckable(true);
+ m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ m_statusRefresh->setIcon(MMC->getThemedIcon("refresh"));
+ layout->addWidget(m_statusRefresh);
+
+ setLayout(layout);
+
+ // Start status checker
+ m_statusChecker.reset(new StatusChecker());
+ {
+ auto reloader = m_statusChecker.get();
+ connect(reloader, &StatusChecker::statusChanged, this, &ServerStatus::StatusChanged);
+ connect(reloader, &StatusChecker::statusLoading, this, &ServerStatus::StatusReloading);
+ connect(m_statusRefresh, &QAbstractButton::clicked, this, &ServerStatus::reloadStatus);
+ m_statusChecker->startTimer(60000);
+ reloadStatus();
+ }
+}
+
+ServerStatus::~ServerStatus()
+{
+}
+
+void ServerStatus::reloadStatus()
+{
+ m_statusChecker->reloadStatus();
+}
+
+void ServerStatus::addLine()
+{
+ layout->addWidget(new LineSeparator(this, Qt::Vertical));
+}
+
+void ServerStatus::addStatus(QString key, QString name)
+{
+ {
+ auto label = new IconLabel(this, badIcon, QSize(16, 16));
+ label->setToolTip(key);
+ serverLabels[key] = label;
+ layout->addWidget(label);
+ }
+ {
+ auto label = new QLabel(this);
+ label->setText(name);
+ label->setToolTip(key);
+ layout->addWidget(label);
+ }
+}
+
+void ServerStatus::setStatus(QString key, int value)
+{
+ if (!serverLabels.contains(key))
+ return;
+ IconLabel *label = serverLabels[key];
+ switch(value)
+ {
+ case 0:
+ label->setIcon(goodIcon);
+ break;
+ case 1:
+ label->setIcon(yellowIcon);
+ break;
+ default:
+ case 2:
+ label->setIcon(badIcon);
+ break;
+ }
+}
+
+void ServerStatus::StatusChanged(const QMap<QString, QString> statusEntries)
+{
+ auto convertStatus = [&](QString status)->int
+ {
+ if (status == "green")
+ return 0;
+ else if (status == "yellow")
+ return 1;
+ else if (status == "red")
+ return 2;
+ return 2;
+ }
+ ;
+ auto iter = statusEntries.begin();
+ while (iter != statusEntries.end())
+ {
+ QString key = iter.key();
+ auto value = convertStatus(iter.value());
+ setStatus(key, value);
+ iter++;
+ }
+}
+
+void ServerStatus::StatusReloading(bool is_reloading)
+{
+ m_statusRefresh->setChecked(is_reloading);
+}
diff --git a/application/widgets/ServerStatus.h b/application/widgets/ServerStatus.h
new file mode 100644
index 00000000..fdd43677
--- /dev/null
+++ b/application/widgets/ServerStatus.h
@@ -0,0 +1,37 @@
+#pragma once
+#include <QString>
+#include <QWidget>
+#include <QMap>
+#include <QIcon>
+#include <memory>
+
+class IconLabel;
+class QToolButton;
+class QHBoxLayout;
+class StatusChecker;
+
+class ServerStatus: public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ServerStatus(QWidget *parent = nullptr, Qt::WindowFlags f = 0);
+ virtual ~ServerStatus();
+ ;
+public slots:
+ void reloadStatus();
+ void StatusChanged(const QMap<QString, QString> statuses);
+ void StatusReloading(bool is_reloading);
+
+private: /* methods */
+ void addLine();
+ void addStatus(QString key, QString name);
+ void setStatus(QString key, int value);
+private: /* data */
+ QHBoxLayout * layout = nullptr;
+ QToolButton *m_statusRefresh = nullptr;
+ QMap<QString, IconLabel *> serverLabels;
+ QIcon goodIcon;
+ QIcon yellowIcon;
+ QIcon badIcon;
+ std::shared_ptr<StatusChecker> m_statusChecker;
+};
diff --git a/application/widgets/VersionListView.cpp b/application/widgets/VersionListView.cpp
new file mode 100644
index 00000000..fc0bcd0a
--- /dev/null
+++ b/application/widgets/VersionListView.cpp
@@ -0,0 +1,147 @@
+/* Copyright 2013-2015 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 <QHeaderView>
+#include <QApplication>
+#include <QMouseEvent>
+#include <QDrag>
+#include <QPainter>
+#include "VersionListView.h"
+#include "Common.h"
+
+VersionListView::VersionListView(QWidget *parent)
+ :QTreeView ( parent )
+{
+ m_emptyString = tr("No versions are currently available.");
+}
+
+void VersionListView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ if(!m_itemCount)
+ viewport()->update();
+ m_itemCount += end-start+1;
+ QTreeView::rowsInserted(parent, start, end);
+}
+
+
+void VersionListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ m_itemCount -= end-start+1;
+ if(!m_itemCount)
+ viewport()->update();
+ QTreeView::rowsInserted(parent, start, end);
+}
+
+void VersionListView::setModel(QAbstractItemModel *model)
+{
+ m_itemCount = model->rowCount();
+ if(!m_itemCount)
+ viewport()->update();
+ QTreeView::setModel(model);
+}
+
+void VersionListView::reset()
+{
+ if(model())
+ {
+ m_itemCount = model()->rowCount();
+ }
+ viewport()->update();
+ QTreeView::reset();
+}
+
+void VersionListView::setEmptyString(QString emptyString)
+{
+ m_emptyString = emptyString;
+ if(!m_itemCount)
+ {
+ viewport()->update();
+ }
+}
+
+void VersionListView::paintEvent(QPaintEvent *event)
+{
+ if(m_itemCount)
+ {
+ QTreeView::paintEvent(event);
+ }
+ else
+ {
+ paintInfoLabel(event);
+ }
+}
+
+void VersionListView::paintInfoLabel(QPaintEvent *event)
+{
+ //calculate the rect for the overlay
+ QPainter painter(viewport());
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ QFont font("sans", 20);
+ font.setBold(true);
+
+ QRect bounds = viewport()->geometry();
+ bounds.moveTop(0);
+ QTextLayout layout(m_emptyString, font);
+ qreal height = 0.0;
+ qreal widthUsed = 0.0;
+ QStringList lines = viewItemTextLayout(layout, bounds.width() - 20, height, widthUsed);
+ QRect rect (0,0, widthUsed, height);
+ rect.setWidth(rect.width()+20);
+ rect.setHeight(rect.height()+20);
+ rect.moveCenter(bounds.center());
+ //check if we are allowed to draw in our area
+ if (!event->rect().intersects(rect)) {
+ return;
+ }
+ //draw the letter of the topmost item semitransparent in the middle
+ QColor background = QApplication::palette().color(QPalette::Foreground);
+ QColor foreground = QApplication::palette().color(QPalette::Base);
+ /*
+ background.setAlpha(128 - scrollFade);
+ foreground.setAlpha(128 - scrollFade);
+ */
+ painter.setBrush(QBrush(background));
+ painter.setPen(foreground);
+ painter.drawRoundedRect(rect, 5.0, 5.0);
+ foreground.setAlpha(190);
+ painter.setPen(foreground);
+ painter.setFont(font);
+ painter.drawText(rect, Qt::AlignCenter, lines.join("\n"));
+
+}
+
+/*
+void ModListView::setModel ( QAbstractItemModel* model )
+{
+ QTreeView::setModel ( model );
+ auto head = header();
+ head->setStretchLastSection(false);
+ // HACK: this is true for the checkbox column of mod lists
+ auto string = model->headerData(0,head->orientation()).toString();
+ if(!string.size())
+ {
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+ else
+ {
+ head->setSectionResizeMode(0, QHeaderView::Stretch);
+ for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+}
+*/
diff --git a/application/widgets/VersionListView.h b/application/widgets/VersionListView.h
new file mode 100644
index 00000000..f33a6fdb
--- /dev/null
+++ b/application/widgets/VersionListView.h
@@ -0,0 +1,43 @@
+/* Copyright 2013-2015 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 <QTreeView>
+
+class Mod;
+
+class VersionListView : public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit VersionListView(QWidget *parent = 0);
+ virtual void paintEvent(QPaintEvent *event) override;
+ void setEmptyString(QString emptyString);
+ virtual void setModel ( QAbstractItemModel* model );
+
+public slots:
+ virtual void reset() override;
+
+protected slots:
+ virtual void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) override;
+ virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
+
+private: /* methods */
+ void paintInfoLabel(QPaintEvent *event);
+
+private: /* variables */
+ int m_itemCount = 0;
+ QString m_emptyString;
+};