From 17a64fdade83f237aefc394ef6f103ab4e697eb9 Mon Sep 17 00:00:00 2001 From: baydam Date: Mon, 29 Feb 2016 16:44:26 +0000 Subject: [PATCH] Work In Progress --- MM.h | 7 + MainWindow.cpp | 301 +++++++++---------- MainWindow.h | 14 +- MappingItemDelegate.cpp | 157 ++++++++++ MappingItemDelegate.h | 55 ++++ MappingListModel.cpp | 181 +++++++++++ MappingListModel.h | 76 +++++ mapmap.pro | 6 +- mapmap.qrc | 5 + resources/images/icons/small/delete.png | Bin 0 -> 491 bytes resources/images/icons/small/delete_w.png | Bin 0 -> 413 bytes resources/images/icons/small/duplicate.png | Bin 0 -> 618 bytes resources/images/icons/small/duplicate_w.png | Bin 0 -> 539 bytes resources/images/icons/small/lock.png | Bin 0 -> 747 bytes resources/images/icons/small/lock_w.png | Bin 0 -> 587 bytes resources/images/icons/small/solo.png | Bin 0 -> 784 bytes resources/images/icons/small/solo_w.png | Bin 0 -> 612 bytes resources/images/icons/small/visible.png | Bin 0 -> 1217 bytes resources/images/icons/small/visible_w.png | Bin 0 -> 1066 bytes resources/qss/mapmap.qss | 2 +- 20 files changed, 641 insertions(+), 163 deletions(-) create mode 100644 MappingItemDelegate.cpp create mode 100644 MappingItemDelegate.h create mode 100644 MappingListModel.cpp create mode 100644 MappingListModel.h create mode 100644 resources/images/icons/small/delete.png create mode 100644 resources/images/icons/small/delete_w.png create mode 100644 resources/images/icons/small/duplicate.png create mode 100644 resources/images/icons/small/duplicate_w.png create mode 100644 resources/images/icons/small/lock.png create mode 100644 resources/images/icons/small/lock_w.png create mode 100644 resources/images/icons/small/solo.png create mode 100644 resources/images/icons/small/solo_w.png create mode 100644 resources/images/icons/small/visible.png create mode 100644 resources/images/icons/small/visible_w.png diff --git a/MM.h b/MM.h index 0816a9d..87bda9e 100644 --- a/MM.h +++ b/MM.h @@ -86,6 +86,13 @@ public: static const int MESH_SUBDIVISION_MAX_DEPTH = (-1); static const int ELLIPSE_N_TRIANGLES = 100; // n triangles used to draw an ellipse static const int VERTEX_MOVES_STEP = 25; + + // Enumerations + enum ItemColumn { + MuteColunm, + IconAndNameColum, + GroupButtonColum + }; }; #endif diff --git a/MainWindow.cpp b/MainWindow.cpp index 790fb78..64398a5 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -131,107 +131,61 @@ void MainWindow::handlePaintItemSelectionChanged() updateCanvases(); } -void MainWindow::handleMappingItemSelectionChanged() +void MainWindow::mappingItemSelectionChanged(const QModelIndex &index) { - if (mappingList->selectedItems().empty()) - { - removeCurrentMapping(); - /* Disable some menus and buttons when - * no mapping was selected */ - sourceCanvas->enableZoomToolBar(false); - sourceMenu->setEnabled(false); - destinationCanvas->enableZoomToolBar(false); - destinationMenu->setEnabled(false); - } - else - { - QListWidgetItem* item = mappingList->currentItem(); - currentSelectedItem = item; + // Set current paint and mappings. + uid mappingId = mappingListModel->getItemId(index); + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); + uid paintId = mapping->getPaint()->getId(); + setCurrentMapping(mappingId); + setCurrentPaint(paintId); + // Enable some menus and buttons + sourceCanvas->enableZoomToolBar(true); + sourceMenu->setEnabled(true); + destinationCanvas->enableZoomToolBar(true); + destinationMenu->setEnabled(true); - // Set current paint and mappings. - uid mappingId = getItemId(*item); - Mapping::ptr mapping = mappingManager->getMappingById(mappingId); - uid paintId = mapping->getPaint()->getId(); - setCurrentMapping(mappingId); - setCurrentPaint(paintId); - // Enable some menus and buttons - sourceCanvas->enableZoomToolBar(true); - sourceMenu->setEnabled(true); - destinationCanvas->enableZoomToolBar(true); - destinationMenu->setEnabled(true); - } - - // Update canvases. - updateCanvases(); +// // Update canvases. +// updateCanvases(); } -void MainWindow::handleMappingItemChanged(QListWidgetItem* item) +void MainWindow::handleMappingItemChanged(const QModelIndex &index) { // Toggle visibility of mapping depending on checkbox of item. - uid mappingId = getItemId(*item); - setMappingVisible(mappingId, item->checkState() == Qt::Checked); -} + uid mappingId = mappingListModel->getItemId(index); + setMappingVisible(mappingId, index.data(Qt::CheckStateRole) == Qt::Checked); + } void MainWindow::handleMappingIndexesMoved() { // Reorder mappings. - QVector newOrder; - for (int row=mappingList->count()-1; row>=0; row--) + //QVector newOrder; + for (int row=mappingList->model()->rowCount()-1; row>=0; row--) { - uid layerId = mappingList->item(row)->data(Qt::UserRole).toInt(); - newOrder.push_back(layerId); + //uid layerId = mappingList->item(row)->data(Qt::UserRole).toInt(); + //newOrder.push_back(layerId); } - mappingManager->reorderMappings(newOrder); + //mappingManager->reorderMappings(newOrder); // Update canvases according to new order. updateCanvases(); } -void MainWindow::handleItemSelected(QListWidgetItem* item) +void MainWindow::handlePaintItemSelected(QListWidgetItem* item) { Q_UNUSED(item); // Change currently selected item. currentSelectedItem = item; } -//void MainWindow::handleItemDoubleClicked(QListWidgetItem* item) -//{ -// // Change currently selected item. -// Paint::ptr paint = mappingManager->getPaintById(getItemId(*item)); -// uid curMappingId = getCurrentMappingId(); -// removeCurrentMapping(); -// removeCurrentPaint(); -// -// //qDebug() << "DOUBLE CLICK! " << endl; -// videoTimer->stop(); -// if (paint->getType() == "media") { -// QString fileName = QFileDialog::getOpenFileName(this, -// tr("Import media source file"), "."); -// // Restart video playback. XXX Hack -// videoTimer->start(); -// if (!fileName.isEmpty()) -// importMediaFile(fileName, paint, false); -// } -// if (paint->getType() == "image") { -// QString fileName = QFileDialog::getOpenFileName(this, -// tr("Import media source file"), "."); -// // Restart video playback. XXX Hack -// videoTimer->start(); -// if (!fileName.isEmpty()) -// importMediaFile(fileName, paint, true); -// } -// else if (paint->getType() == "color") { -// // Pop-up color-choosing dialog to choose color paint. -// QColor initialColor; -// QColor color = QColorDialog::getColor(initialColor, this); -// videoTimer->start(); -// if (color.isValid()) -// addColorPaint(color, paint); -// } -// -// if (curMappingId != NULL_UID) -// setCurrentMapping(curMappingId); -//} +void MainWindow::handleMappingItemSelected(const QModelIndex &index) +{ + if (index.isValid()) { + if (index.column() == MM::MuteColunm) { + mappingListModel->setVisibility(index); + } + } +} void MainWindow::handlePaintChanged(Paint::ptr paint) { // Change currently selected item. @@ -750,7 +704,7 @@ void MainWindow::deleteItem() if (isMappingTabSelected) //currentSelectedItem->listWidget() == mappingList) { // Delete mapping. - undoStack->push(new DeleteMappingCommand(this, getItemId(*mappingList->currentItem()))); + undoStack->push(new DeleteMappingCommand(this, mappingList->currentIndex().data(Qt::UserRole).toInt())); //currentSelectedItem = NULL; } else if (isPaintTabSelected) //currentSelectedItem->listWidget() == paintList) @@ -768,9 +722,9 @@ void MainWindow::deleteItem() void MainWindow::duplicateMappingItem() { - if (currentSelectedItem) + if (currentSelectedIndex.isValid()) { - duplicateMapping(getItemId(*mappingList->currentItem())); + duplicateMapping(currentMappingItemId()); } else { @@ -780,9 +734,9 @@ void MainWindow::duplicateMappingItem() void MainWindow::deleteMappingItem() { - if (currentSelectedItem) + if (currentSelectedIndex.isValid()) { - deleteMapping(getItemId(*mappingList->currentItem())); + deleteMapping(currentMappingItemId()); } else { @@ -793,42 +747,42 @@ void MainWindow::deleteMappingItem() void MainWindow::renameMappingItem() { // Set current item editable and rename it - QListWidgetItem* item = mappingList->currentItem(); - item->setFlags(item->flags() | Qt::ItemIsEditable); - // Used by context menu - mappingList->editItem(item); +// QListWidgetItem* item = mappingList->currentItem(); +// item->setFlags(item->flags() | Qt::ItemIsEditable); +// // Used by context menu +// mappingList->editItem(item); // Switch to mapping tab. contentTab->setCurrentWidget(mappingSplitter); } void MainWindow::setMappingitemLocked(bool locked) { - setMappingLocked(getItemId(*mappingList->currentItem()), locked); + setMappingLocked(currentMappingItemId(), locked); } void MainWindow::setMappingitemVisible(bool visible) { - setMappingVisible(getItemId(*mappingList->currentItem()), !visible); + setMappingVisible(currentMappingItemId(), !visible); } void MainWindow::setMappingItemSolo(bool solo) { - setMappingSolo(getItemId(*mappingList->currentItem()), solo); + setMappingSolo(currentMappingItemId(), solo); } void MainWindow::renameMapping(uid mappingId, const QString &name) { - Mapping::ptr mapping = mappingManager->getMappingById(mappingId); - if (!mapping.isNull()) { - getItemFromId(*mappingList, mappingId)->setText(name); - mapping->setName(name); - } +// Mapping::ptr mapping = mappingManager->getMappingById(mappingId); +// if (!mapping.isNull()) { +// getItemFromId(*mappingList, mappingId)->setText(name); +// mapping->setName(name); +// } } void MainWindow::mappingListEditEnd(QWidget *editor) { QString name = reinterpret_cast(editor)->text(); - renameMapping(getItemId(*mappingList->currentItem()), name); + renameMapping(currentMappingItemId(), name); } void MainWindow::deletePaintItem() @@ -893,7 +847,8 @@ bool MainWindow::clearProject() removeCurrentMapping(); // Empty list widgets. - mappingList->clear(); + for (int i = 0; i < mappingList->model()->rowCount(); i++) + mappingList->model()->removeRow(i); paintList->clear(); // Clear property panel. @@ -1177,9 +1132,8 @@ void MainWindow::setMappingVisible(uid mappingId, bool visible) { mapping->setVisible(visible); // Change list item check state - QListWidgetItem* item = getItemFromId(*mappingList, mappingId); - Q_ASSERT( item ); - item->setCheckState(visible ? Qt::Checked : Qt::Unchecked ); + //QModelIndex index = mappingListModel->getIndexFromId(mappingId); + //mappingListModel->setData(index, visible, Qt::CheckStateRole); // Update canvases. updateCanvases(); } @@ -1322,13 +1276,26 @@ void MainWindow::createLayout() paintPropertyPanel->setMinimumHeight(PAINT_PROPERTY_PANEL_MINIMUM_HEIGHT); // Create mapping list. - mappingList = new QListWidget; + mappingList = new QTableView; mappingList->setSelectionMode(QAbstractItemView::SingleSelection); + mappingList->setSelectionBehavior(QAbstractItemView::SelectRows); mappingList->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - //layerList->setDragDropMode(QAbstractItemView::DragDrop); mappingList->setDefaultDropAction(Qt::MoveAction); mappingList->setDragDropMode(QAbstractItemView::InternalMove); + mappingList->setEditTriggers(QAbstractItemView::DoubleClicked); mappingList->setMinimumHeight(MAPPING_LIST_MINIMUM_HEIGHT); + mappingList->setContentsMargins(0, 0, 0, 0); + // Set view delegate + mappingList->setItemDelegate(new MappingItemDelegate(this)); + mappingListModel = new MappingListModel(this); + mappingList->setModel(mappingListModel); + // Pimp Mapping table widget + mappingList->horizontalHeader()->setStretchLastSection(true); + mappingList->horizontalHeader()->setHighlightSections(false); + mappingList->setFrameShape(QFrame::NoFrame); + mappingList->horizontalHeader()->setVisible(false); + mappingList->verticalHeader()->setVisible(false); + mappingList->setMouseTracking(true);// Important // Create property panel. mappingPropertyPanel = new QStackedWidget; @@ -1862,12 +1829,12 @@ void MainWindow::createMappingContextMenu() mappingContextMenu->addAction(mappingSoloAction); // Set context menu policy - mappingList->setContextMenuPolicy(Qt::CustomContextMenu); + mappingList->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); destinationCanvas->setContextMenuPolicy(Qt::CustomContextMenu); outputWindow->setContextMenuPolicy(Qt::CustomContextMenu); // Context Menu Connexions - connect(mappingList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); + connect(mappingList->horizontalHeader(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); connect(destinationCanvas, SIGNAL(shapeContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); connect(outputWindow->getCanvas(), SIGNAL(shapeContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); } @@ -2427,14 +2394,19 @@ void MainWindow::addMappingItem(uid mappingId) contentTab->setCurrentWidget(mappingSplitter); // Add item to layerList widget. - QListWidgetItem* item = new QListWidgetItem(label); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(Qt::Checked); - setItemId(*item, mappingId); // TODO: could possibly be replaced by a Paint pointer - item->setIcon(icon); - item->setSizeHint(QSize(item->sizeHint().width(), MainWindow::SHAPE_LIST_ITEM_HEIGHT)); - mappingList->insertItem(0, item); - mappingList->setCurrentItem(item); +// QListWidgetItem* item = new QListWidgetItem(label); +// item->setFlags(item->flags() | Qt::ItemIsUserCheckable); +// item->setCheckState(Qt::Checked); +// setItemId(*item, mappingId); // TODO: could possibly be replaced by a Paint pointer +// item->setIcon(icon); +// item->setSizeHint(QSize(item->sizeHint().width(), MainWindow::SHAPE_LIST_ITEM_HEIGHT)); +// mappingList->insertItem(0, item); +// mappingList->setCurrentItem(item); + mappingListModel->addItem(icon, label, mappingId); + mappingListModel->updateModel(); + mappingList->resizeRowsToContents(); + mappingList->resizeColumnsToContents(); + setCurrentMapping(mappingId); // Disable Test signal when add Shapes enableTestSignal(false); @@ -2461,16 +2433,18 @@ void MainWindow::removeMappingItem(uid mappingId) mappingPropertyPanel->removeWidget(mappers[mappingId]->getPropertiesEditor()); mappers.remove(mappingId); - // Remove widget from mappingList. - int row = getItemRowFromId(*mappingList, mappingId); - Q_ASSERT( row >= 0 ); - QListWidgetItem* item = mappingList->takeItem(row); - if (item == currentSelectedItem) - currentSelectedItem = NULL; - delete item; + + +// // Remove widget from mappingList. +// QModelIndex index = getIndexFromId(*mappingListModel, mappingId); +// //Q_ASSERT( row >= 0 ); +// QStandardItem* item = mappingListModel->itemFromIndex(index); +// if (item == currentSelectedMappingItem) +// currentSelectedMappingItem= NULL; +// delete item; // Update list. - mappingList->update(); + //mappingList->update(); // Update everything. updateCanvases(); @@ -2647,7 +2621,7 @@ void MainWindow::enableStickyVertices(bool value) void MainWindow::showMappingContextMenu(const QPoint &point) { QWidget *objectSender = dynamic_cast(sender()); - uid mappingId = getItemId(*mappingList->currentItem()); + uid mappingId = currentMappingItemId(); Mapping::ptr mapping = mappingManager->getMappingById(mappingId); // Switch to right action check state @@ -2655,7 +2629,7 @@ void MainWindow::showMappingContextMenu(const QPoint &point) mappingMuteAction->setChecked(!mapping->isVisible()); mappingSoloAction->setChecked(mapping->isSolo()); - if (objectSender != NULL && mappingList->count() > 0) + if (objectSender != NULL && mappingListModel->rowCount() > 0) mappingContextMenu->exec(objectSender->mapToGlobal(point)); } @@ -2678,13 +2652,10 @@ void MainWindow::connectProjectWidgets() this, SLOT(handlePaintItemSelectionChanged())); connect(paintList, SIGNAL(itemPressed(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); - - // connect(paintList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), - // this, SLOT(handleItemDoubleClicked(QListWidgetItem*))); + this, SLOT(handlePaintItemSelected(QListWidgetItem*))); connect(paintList, SIGNAL(itemActivated(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + this, SLOT(handlePaintItemSelected(QListWidgetItem*))); // Rename Paint with double click connect(paintList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(renamePaintItem())); @@ -2692,29 +2663,29 @@ void MainWindow::connectProjectWidgets() connect(paintList->itemDelegate(), SIGNAL(commitData(QWidget*)), this, SLOT(paintListEditEnd(QWidget*))); - connect(mappingList, SIGNAL(itemSelectionChanged()), - this, SLOT(handleMappingItemSelectionChanged())); + connect(mappingList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), + this, SLOT(mappingItemSelectionChanged(QModelIndex))); - connect(mappingList, SIGNAL(itemChanged(QListWidgetItem*)), - this, SLOT(handleMappingItemChanged(QListWidgetItem*))); + connect(mappingListModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(handleMappingItemChanged(QModelIndex))); - connect(mappingList, SIGNAL(itemPressed(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + connect(mappingList, SIGNAL(pressed(QModelIndex)), + this, SLOT(handleMappingItemSelected(const QModelIndex&))); - connect(mappingList, SIGNAL(itemActivated(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + connect(mappingList, SIGNAL(activated(const QModelIndex&)), + this, SLOT(handleMappingItemSelected(const QModelIndex&))); - connect(mappingList, SIGNAL(indexesMoved(const QModelIndexList&)), - this, SLOT(handleMappingIndexesMoved())); +// connect(mappingList, SIGNAL(indexesMoved(const QModelIndexList&)), +// this, SLOT(handleMappingIndexesMoved())); - connect(mappingList->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex &, int)), - this, SLOT(handleMappingIndexesMoved())); - // Rename mapping with double click - connect(mappingList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), - this, SLOT(renameMappingItem())); - // When finish to edit mapping item - connect(mappingList->itemDelegate(), SIGNAL(commitData(QWidget*)), - this, SLOT(mappingListEditEnd(QWidget*))); +// connect(mappingList->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex &, int)), +// this, SLOT(handleMappingIndexesMoved())); +// // Rename mapping with double click +// connect(mappingList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), +// this, SLOT(renameMappingItem())); +// // When finish to edit mapping item +// connect(mappingList->itemDelegate(), SIGNAL(commitData(QWidget*)), +// this, SLOT(mappingListEditEnd(QWidget*))); } void MainWindow::disconnectProjectWidgets() @@ -2723,25 +2694,34 @@ void MainWindow::disconnectProjectWidgets() this, SLOT(handlePaintItemSelectionChanged())); disconnect(paintList, SIGNAL(itemPressed(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + this, SLOT(handlePaintItemSelected(QListWidgetItem*))); disconnect(paintList, SIGNAL(itemActivated(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + this, SLOT(handlePaintItemSelected(QListWidgetItem*))); - disconnect(mappingList, SIGNAL(itemSelectionChanged()), - this, SLOT(handleMappingItemSelectionChanged())); + disconnect(mappingList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), + this, SLOT(mappingItemSelectionChanged(QModelIndex))); - disconnect(mappingList, SIGNAL(itemChanged(QListWidgetItem*)), - this, SLOT(handleMappingItemChanged(QListWidgetItem*))); + disconnect(mappingListModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(handleMappingItemChanged(QStandardItem*))); - disconnect(mappingList, SIGNAL(itemPressed(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + disconnect(mappingList, SIGNAL(pressed(const QModelIndex&)), + this, SLOT(handleMappingItemSelected(const QModelIndex&))); - disconnect(mappingList, SIGNAL(itemActivated(QListWidgetItem*)), - this, SLOT(handleItemSelected(QListWidgetItem*))); + disconnect(mappingList, SIGNAL(activated(const QModelIndex&)), + this, SLOT(handleMappingItemSelected(const QModelIndex&))); + + disconnect(mappingList->model(), SIGNAL(indexesMoved(const QModelIndexList&)), + this, SLOT(handleMappingIndexesMoved())); disconnect(mappingList->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex &, int)), - this, SLOT(handleMappingIndexesMoved())); + this, SLOT(handleMappingIndexesMoved())); + + disconnect(mappingList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + this, SLOT(renameMappingItem())); + + disconnect(mappingList->itemDelegate(), SIGNAL(commitData(QWidget*)), + this, SLOT(mappingListEditEnd(QWidget*))); } uid MainWindow::getItemId(const QListWidgetItem& item) @@ -2789,6 +2769,11 @@ QIcon MainWindow::createImageIcon(const QString& filename) { return QIcon(filename); } +uid MainWindow::currentMappingItemId() const +{ + return mappingList->selectionModel()->selectedRows().first().data(Qt::UserRole).toInt(); +} + void MainWindow::setCurrentPaint(int uid) { if (uid == NULL_UID) @@ -2810,7 +2795,9 @@ void MainWindow::setCurrentMapping(int uid) else { if (currentMappingId != uid) { currentMappingId = uid; - mappingList->setCurrentRow( getItemRowFromId(*mappingList, uid) ); + currentSelectedIndex = mappingListModel->selectedIndex(mappingListModel->getItemRowFromId(uid)); + mappingList->selectionModel()->select(currentSelectedIndex, QItemSelectionModel::Select); + mappingList->setCurrentIndex(currentSelectedIndex); mappingPropertyPanel->setCurrentWidget(mappers[uid]->getPropertiesEditor()); } _hasCurrentMapping = true; diff --git a/MainWindow.h b/MainWindow.h index 7dbda87..197f7af 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -43,6 +43,8 @@ #include "ConsoleWindow.h" #include "MappingManager.h" +#include "MappingItemDelegate.h" +#include "MappingListModel.h" #include "qtpropertymanager.h" #include "qtvariantproperty.h" @@ -111,10 +113,11 @@ private slots: // Widget callbacks. void handlePaintItemSelectionChanged(); // void handleItemDoubleClicked(QListWidgetItem* item); - void handleMappingItemSelectionChanged(); - void handleMappingItemChanged(QListWidgetItem* item); + void mappingItemSelectionChanged(const QModelIndex &index); + void handleMappingItemChanged(const QModelIndex &index); void handleMappingIndexesMoved(); - void handleItemSelected(QListWidgetItem* item); + void handlePaintItemSelected(QListWidgetItem* item); + void handleMappingItemSelected(const QModelIndex &index); void handlePaintChanged(Paint::ptr paint); void addMesh(); @@ -276,6 +279,7 @@ private: static QIcon createColorIcon(const QColor& color); static QIcon createFileIcon(const QString& filename); static QIcon createImageIcon(const QString& filename); + uid currentMappingItemId() const; // GUI elements. //////////////////////////////////////////////////////////////////////////////////////// @@ -355,7 +359,7 @@ private: QStackedWidget* paintPropertyPanel; QSplitter* mappingSplitter; - QListWidget* mappingList; + QTableView* mappingList; QStackedWidget* mappingPropertyPanel; QUndoView* undoView; @@ -385,6 +389,7 @@ private: // Model. MappingManager* mappingManager; + MappingListModel *mappingListModel; // OSC. #ifdef HAVE_OSC @@ -424,6 +429,7 @@ private: // Keeps track of the current selected item, wether it's a paint or mapping. QListWidgetItem* currentSelectedItem; + QModelIndex currentSelectedIndex; QTimer *videoTimer; PreferencesDialog* _preferences_dialog; diff --git a/MappingItemDelegate.cpp b/MappingItemDelegate.cpp new file mode 100644 index 0000000..70ad015 --- /dev/null +++ b/MappingItemDelegate.cpp @@ -0,0 +1,157 @@ +/* + * LayerItemDelegate.h + * + * (c) 2016 Dame Diongue -- baydamd(@)gmail(.)com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "MappingItemDelegate.h" + +MappingItemDelegate::MappingItemDelegate(QObject *parent) : + QStyledItemDelegate(parent) +{ + +} + +MappingItemDelegate::~MappingItemDelegate() +{ + +} + +void MappingItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (index.isValid()) { + QRect rect = option.rect; + int x = rect.x(); + int y = rect.y(); + + if (option.state & QStyle::State_Selected) + painter->fillRect(rect, MM::DARK_GRAY); + + if (index.column() == MM::MuteColunm) { + bool isVisible = index.model()->data(index, Qt::CheckStateRole).toBool(); + if (isVisible) { + QStyleOptionButton mappingMuteButton; + mappingMuteButton.state |= QStyle::State_Enabled; + mappingMuteButton.rect = QRect(x + 7, y + 12, 16, 16); + mappingMuteButton.icon = QIcon(":/visible-mapping"); + mappingMuteButton.iconSize = QSize(16, 16); + + QApplication::style()->drawControl( + QStyle::CE_PushButtonLabel, &mappingMuteButton, painter); + } + } + + if(index.column() == MM::IconAndNameColum) { + // Draw Icon + QIcon mappingIcon = qvariant_cast(index.model()->data(index, Qt::DecorationRole)); + QPixmap iconPixmap = mappingIcon.pixmap(24, 24); + QRect iconRect(x + 5, y, 24, rect.height()); + QApplication::style()->drawItemPixmap(painter, iconRect, + Qt::AlignLeft | Qt::AlignVCenter, iconPixmap); + + // Draw Text + QString mappingName = index.model()->data(index, Qt::DisplayRole).toString(); + QRect textRect(x + 40, y, rect.width() - 40, rect.height()); + QPalette textColor = QPalette(MM::WHITE); + + QApplication::style()->drawItemText(painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, + textColor, true, mappingName, QPalette::Window); + } + + if (index.column() == MM::GroupButtonColum) { + // Draw Buttons + QStyleOptionButton mappingSoloButton; + mappingSoloButton.state |= QStyle::State_Enabled; + mappingSoloButton.rect = QRect(x + 10, y + 12, 16, 16); + mappingSoloButton.icon = QIcon(":/solo-mapping"); + mappingSoloButton.iconSize = QSize(16, 16); + + QStyleOptionButton mappingLockButton; + mappingLockButton.state |= QStyle::State_Enabled; + mappingLockButton.rect = QRect(x + 40, y + 12, 16, 16); + mappingLockButton.icon = QIcon(":/lock-mapping"); + mappingLockButton.iconSize = QSize(16, 16); + + QStyleOptionButton mappingDuplicateButton; + mappingDuplicateButton.state |= QStyle::State_Enabled; + mappingDuplicateButton.rect = QRect(x + 70, y + 12, 16, 16); + mappingDuplicateButton.icon = QIcon(":/duplicate-mapping"); + mappingDuplicateButton.iconSize = QSize(16, 16); + + QStyleOptionButton mappingDeleteButton; + mappingDeleteButton.state |= QStyle::State_Enabled; + mappingDeleteButton.rect = QRect(x + 100, y + 12, 16, 16); + mappingDeleteButton.icon = QIcon(":/delete-mapping"); + mappingDeleteButton.iconSize = QSize(16, 16); + + QApplication::style()->drawControl( + QStyle::CE_PushButtonLabel, &mappingSoloButton, painter); + QApplication::style()->drawControl( + QStyle::CE_PushButtonLabel, &mappingLockButton, painter); + QApplication::style()->drawControl( + QStyle::CE_PushButtonLabel, &mappingDuplicateButton, painter); + QApplication::style()->drawControl( + QStyle::CE_PushButtonLabel, &mappingDeleteButton, painter); + + } + } else { + QStyledItemDelegate::paint(painter, option, index); + } +} + +QWidget *MappingItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + qDebug() << "createEditor"; + if (index.column() == MM::IconAndNameColum) { + QLineEdit *editor = new QLineEdit(parent); + editor->setFixedHeight(option.rect.height()); + editor->setContentsMargins(option.rect.x() + 4, 0, 0, 0); + return editor; + } else + return 0; +} + +void MappingItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + QString value = index.model()->data(index, Qt::EditRole).toString(); + + QLineEdit *nameEdit = static_cast(editor); // TODO: use reinterpret_cast instead static_cast + nameEdit->setText(value); + qDebug() << "setEditorData"; +} + +void MappingItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + qDebug() << "setModelData"; + QLineEdit *nameEdit = static_cast(editor); // TODO: use reinterpret_cast instead static_cast + model->setData(index, nameEdit->text(), Qt::EditRole); +} + +void MappingItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index); + editor->setGeometry(option.rect); +} + +bool MappingItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + if (event->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = static_cast(event); + if (index.column() == MM::GroupButtonColum) + qDebug() << "Mouse pos" << mouseEvent->pos(); + } + return false; +} diff --git a/MappingItemDelegate.h b/MappingItemDelegate.h new file mode 100644 index 0000000..2d594c7 --- /dev/null +++ b/MappingItemDelegate.h @@ -0,0 +1,55 @@ +/* + * MappingItemDelegate.h + * + * (c) 2016 Dame Diongue -- baydamd(@)gmail(.)com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MAPPINGITEMDELEGATE_H +#define MAPPINGITEMDELEGATE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "MM.h" + +class MappingItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + MappingItemDelegate(QObject *parent = 0); + ~MappingItemDelegate(); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const /*Q_DECL_OVERRIDE*/; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const /*Q_DECL_OVERRIDE*/; + void setEditorData(QWidget *editor, const QModelIndex &index) const /*Q_DECL_OVERRIDE*/; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const /*Q_DECL_OVERRIDE*/; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &index) const /*Q_DECL_OVERRIDE*/; +protected: + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index); +}; + +#endif // MAPPINGITEMDELEGATE_H diff --git a/MappingListModel.cpp b/MappingListModel.cpp new file mode 100644 index 0000000..9ad3e2b --- /dev/null +++ b/MappingListModel.cpp @@ -0,0 +1,181 @@ +/* + * MappingListModel.h + * + * (c) 2016 Dame Diongue -- baydamd(@)gmail(.)com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "MappingListModel.h" + +MappingListModel::MappingListModel(QObject *parent) : + QAbstractTableModel(parent) +{ + +} + +MappingListModel::~MappingListModel() +{ + +} + +int MappingListModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() && parent.column() != 0) ? 0 : mappingList.size(); +} + +int MappingListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 3; +} + +QVariant MappingListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.column() == MM::MuteColunm) { + + if (role == Qt::CheckStateRole) + return mappingList.at(index.row()).isVisible ? Qt::Checked : Qt::Unchecked; + + if (role == Qt::SizeHintRole) + return QSize(30, 40); + + } else { + + if (role == Qt::EditRole || role == Qt::DisplayRole) + return QVariant(mappingList.at(index.row()).name); + + if (role == Qt::UserRole) + return QVariant(mappingList.at(index.row()).id); + + if (role == Qt::DecorationRole) + return mappingList.at(index.row()).icon; + + if (role == Qt::SizeHintRole) + return QSize(130, 40); + + if (role == Qt::TextAlignmentRole) + return int(Qt::AlignVCenter); + } + + return QVariant(); +} + +QVariant MappingListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole) + return QString::number(section); + + return QAbstractItemModel::headerData(section, orientation, role); +} + +Qt::ItemFlags MappingListModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + if (index.column() == MM::MuteColunm) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | + Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;; + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | + Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; +} + +bool MappingListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if (role == Qt::CheckStateRole && value.type() == QVariant::Bool) { + mappingList[index.row()].isVisible = value.toBool(); + emit dataChanged(index, index); + return true; + } + + if (role == Qt::EditRole && index.column() == MM::IconAndNameColum) { + mappingList[index.row()].name = value.toString(); + emit dataChanged(index, index); + return true; + } + + return false; +} + +void MappingListModel::removeItem(int index) +{ + QList::iterator it = mappingList.begin(); + mappingList.erase(it + index); +} + +void MappingListModel::addItem(const QIcon &icon, const QString &name, int id) +{ + MappingItem item; + + item.icon = icon; + item.name = name; + item.isVisible = true; + item.id = id; + mappingList.insert(0, item); +} + +void MappingListModel::updateModel() +{ + beginResetModel(); + endResetModel(); +} + +QModelIndex MappingListModel::selectedIndex(int row) +{ + return this->createIndex(row, 1); +} + +void MappingListModel::setSelectedRow(int row) +{ + selectedRow = row; +} + +int MappingListModel::getSelectedRow() const +{ + return selectedRow; +} + +int MappingListModel::getItemRowFromId(int id) const +{ + for ( int row = 0; row < mappingList.size(); row++) { + int itemId = mappingList.at(row).id; + if (itemId == id) + return row; + } +} + +QModelIndex MappingListModel::getIndexFromId(int id) const +{ + return this->createIndex(getItemRowFromId(id), 0); +} + +int MappingListModel::getItemId(const QModelIndex &index) const +{ + return mappingList.at(index.row()).id; +} + +void MappingListModel::setVisibility(const QModelIndex &index) +{ + if (index.isValid() && index.column() == MM::MuteColunm) { + setData(index, !(mappingList.at(index.row()).isVisible), Qt::CheckStateRole); + } +} diff --git a/MappingListModel.h b/MappingListModel.h new file mode 100644 index 0000000..a4da825 --- /dev/null +++ b/MappingListModel.h @@ -0,0 +1,76 @@ +/* + * MappingListModel.h + * + * (c) 2016 Dame Diongue -- baydamd(@)gmail(.)com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MAPPINGLISTMODEL_H +#define MAPPINGLISTMODEL_H + +#include +#include +#include +#include +#include + +#include "MM.h" + +class MappingListModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + MappingListModel(QObject *parent = 0); + ~MappingListModel(); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + int columnCount(const QModelIndex & parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role); + + void removeItem(int index); + void addItem(const QIcon &icon, const QString &name, int id); + + void updateModel(); + QModelIndex selectedIndex(int row); + + void setSelectedRow(int row); + int getSelectedRow() const; + int getItemRowFromId(int id) const; + int getItemId(const QModelIndex &index) const; + QModelIndex getIndexFromId(int id) const; + +public slots: + void setVisibility(const QModelIndex &index); + +private: + struct MappingItem { + int id; + QIcon icon; + QString name; + bool isVisible; + }; + + QList mappingList; + + int selectedRow; +}; + +#endif // MAPPINGLISTMODEL_H diff --git a/mapmap.pro b/mapmap.pro index 44f1125..ef07215 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -2,7 +2,7 @@ CONFIG += qt debug c++11 TEMPLATE = app VERSION = 0.3.2 TARGET = mapmap -QT += gui opengl xml +QT += gui opengl xml core greaterThan(QT_MAJOR_VERSION, 4): QT += widgets core DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB @@ -17,6 +17,8 @@ HEADERS = \ MainApplication.h \ MainWindow.h \ MappingGui.h \ + MappingItemDelegate.h \ + MappingListModel.h \ MapperGLCanvas.h \ Mapping.h \ MappingManager.h \ @@ -56,6 +58,8 @@ SOURCES = \ MainApplication.cpp \ MainWindow.cpp \ MappingGui.cpp \ + MappingItemDelegate.cpp \ + MappingListModel.cpp \ MapperGLCanvas.cpp \ Mapping.cpp \ MappingManager.cpp \ diff --git a/mapmap.qrc b/mapmap.qrc index 80914a8..871ee18 100644 --- a/mapmap.qrc +++ b/mapmap.qrc @@ -32,5 +32,10 @@ resources/images/icons/zoom_out_w.png resources/images/icons/zoom_fit_w.png resources/fonts/Hack-Regular.otf + resources/images/icons/small/delete_w.png + resources/images/icons/small/lock_w.png + resources/images/icons/small/visible_w.png + resources/images/icons/small/duplicate_w.png + resources/images/icons/small/solo_w.png diff --git a/resources/images/icons/small/delete.png b/resources/images/icons/small/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf8a1c5e8bd92e3da5bfa6817bf07bba737451a GIT binary patch literal 491 zcmV7u3)% zVW>^GQ!c@_^8kSTy(0j?ygi5A^3PU2tJLx2yZ``BP7eV9j?+f9;#mpt0Bk#twe_We zST;M+k2S9D`v8B>RYTMs`Up~h6d(mi0a5^;Zvk=d$#L2qx7+=~b>pG`+*2<$nDbl9 z)5T=W&a7-Q;fq_xV)5S3) zqV?_c^?uBb0`2=5=QS?jWbzhmXJVeF#L-mU5aey}gUhBZft#N}&V=nwl7?f0QsyD+ z3Gz)}f)?!FaCTDH?fJXT+&NQo^3D9n6}xrJ9D*c$GRi*QXZXdcp!M8G$06(NPnk(! zjfZcvOggsp!HqNTl7y#vslVSfvxmdKI;Vst E0JBi0U;qFB literal 0 HcmV?d00001 diff --git a/resources/images/icons/small/duplicate.png b/resources/images/icons/small/duplicate.png new file mode 100644 index 0000000000000000000000000000000000000000..3ae8674aaca8c33cd1282eed4c273318b9c1e6f2 GIT binary patch literal 618 zcmV-w0+s!VP)^01VZH$R`l!7l6-1#Ogdb08oJ|mbKgLg}D@fW$iWx z%ab)`yaaGPg~xZ+f=9N!|68;v>H&dhGNm~H3{$AzkK2?42o+~Sx8i4tOcMgo2w*&$ zt-&;h5~Cf*#nYLE_A_%B`=XKne`T6O7@v5U7}sh!(lwO;9LJR^-fErp2H@Xg%}bI^ z*gGkmKxOO!1_o{>#`(UFcbhx;13a02EZu~%02k{MymKz@{EROAG# z!|f`7swnv5K^@1(Cmn0`FO7Tw&eQ9a^-N|W{iit)f1i{BU=F=(0HK=+0F0w7_N*0S z^M(I=w@RhatJjSFO31+t6=NbTU@iDz+k4+s@-COo0B|#g;EFhlVgLXD07*qoM6N<$ Ef@u^5qW}N^ literal 0 HcmV?d00001 diff --git a/resources/images/icons/small/duplicate_w.png b/resources/images/icons/small/duplicate_w.png new file mode 100644 index 0000000000000000000000000000000000000000..bc5d41cb78f8014bc4acdfe4f581e679c7d3ba02 GIT binary patch literal 539 zcmV+$0_6RPP)^{tsjmhR z83m>Q*DzKMxKm0!$08DuN3qv~X}+Ki_(0N<-&aZ*buI`%ER%l(RH`H9cXBj*o`50{ z3rt%9`#St9iNgSzwp9^%u^h~@h>V2g>(k`Q0RxsbjRTW$0Zi`%?e#iC2+h-ScyLCk zREf^!rZ_s@Z8e1!stW)>sZ`+GQ9F|Xa z2)ibuEx5h8r&^8v*g1~FLO%EN0H-IH+C!)d@VB;Lb8C&2LO#+jkw|n0ATu#$;%*Hf z1iTXfe7{cPt5xns)e#9$0|IZ7jrE<5AzWOVXK#Po8X#y0FMwC;h=%IufKsXe z6oQ;*=dz^JgKf6#bf!CGt@;mFap-$e7oZ*!z>f=H{KKKUUh54nA|5aU3>hYF02QTF d*>GJC=^a@3f?Jc+SgZg5002ovPDHLkV1hHr-lYHl literal 0 HcmV?d00001 diff --git a/resources/images/icons/small/lock.png b/resources/images/icons/small/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..7025fc40d1c198c0556d85969a911757025bc9ab GIT binary patch literal 747 zcmV{K|BOUP-0|*2T)|lkCBiJZ;fGT_wn4a-wgFpo!2uV~z zt|U(m4LPA@1K4)`HbawLAk}~id_v@bse0eHX?z$UKDNkmLBJO^W0)h8~;kv!h;x6Q1WU>zi zdd@3q&vxH!-JYsn$Q&RPditZZZ0smVzXA%jZS|bXu?9Gf!)9urrJjp6zb7?fMGO0Qn*{2*sc% zbS~9AErn;GAow8}^bDX-iQ)%Q!T9JEU?xWtrPxqoi)R4W)x7k)s+;f%Fek^Dt>q0M zS6T=7<0TAQhfj;h!__cT)uzz!^G9>X)_esPJe`ZFyrPUNS1$l?^Tr)UN5AJ5;O@OP zYIoHDU|Ch1YP^(HovgQ@xWxCXT~g}LwoLW_-7nq{jmC*a<8(Q%a}Utka*J4Of>>;V z>#etQ53o{N2gv#w0{DD|YYVV%Pvf!y{=zLV-Cj)@O{)0Bpb|h`MnqWBPbaN;^G&yR dMX3KN{REA-*&5Dj8{+@~002ovPDHLkV1k^9OsxO_ literal 0 HcmV?d00001 diff --git a/resources/images/icons/small/lock_w.png b/resources/images/icons/small/lock_w.png new file mode 100644 index 0000000000000000000000000000000000000000..d0080ccbee6303c47371e5c4a349e0aebeaf05c7 GIT binary patch literal 587 zcmV-R0<`^!P)E^1)&{e1T`S&*dmC2S5Wz30~+ z0C%30<%$jPPxK5>B4G4^4^2`UEw0z_H&AtHEvMfnr}{AWNs{z_dQ$D}ZB-lz15Aw1 zXnU)$JY@HC`nEKF)~AA zkGl(=0leE#lA`Fcu>h-UOLYS@de$w^Bl4!!l z#1L7zBPuS9bzu}kT&N_13*9Ru#HF&*2<#+bWlV^Y)F8DqLHSgqw6@dt+yFN1v@=uc z3eIYB&VA>eckj#qhdAgE_j6d5X@kQdOR?CuWT_rPsRvbW;8}$rMk@qyrBw+8OZ6co zHCMW?aL!+3b6g-7L!tMt|9b$Rj~-1W#RYI2&ym9MRsyx&}pW22fj#H>Mu(GS*8vEL&a)1=Xo3f zf1s+gcHaRi1ZfZ;b;W8J=S+uoGEKa{uKzOqO;7L}o zTY5rX0AJ&ICphjJ7{c%GOnaK#eovFzf9mv!yqo2Mg9qDWDjNp)W7CE};L7F806P|lVKb3Uf{A+IZ^~S=>)aK!#Rknp#YL@x1eW}l97BhYEg!fJY4lkaQK8^b#@6h-$PZB)M*#*Bf{Sm`;sw+uy0C8L2ft zbfi%~l15xE9OZIfa$}QYzPgQ-bh}>ju%r(jqV@1e(pe|3T3c|J%2BGx z{M^!NVkntT3?*50N@WY8_%UGAE-b80000C#0!i-2%c%v6;h#(Y%ph+cSOP3Z(6;Kq>V9NfL0!{gAk+N)e zyL-k9%HedoTR>yH>3j8k-@G&LJ9ExA85qC-{%43gonW`S(kw#G5Q4D+F$$Db3E8Uj z*eP9T`~Cjfzy1>gPW$YcQj5K)l$Z+)NR%ghIEss;lIwukw`;Zs z$AnNTPx|)l`9}tv&N->*k#_{V23T&?bMrpf*XKSylZur~ja(^3!-1cuto)YxeTV4myvkUI4Jjo6c6%O5DUKZe z&Ag0N2%gT$cKqJn?r-XI0uG1EDV5d;4DaPolk9bGCeL==Q#c&qrDdzBuRGjp*W)KR z_d+O%gDHD=W2P@>)PUoBq>2U0yg20uGnVmhV_opYOTj#TM4Fnp#5^-cvhz4jDS*1?b+QO#hPkm z>a3w-$?qO<%6(fvNW;-ccORo;^qTyk%UvbU992to8V6SD8y1acJ)d92=H3 zrcoiD0MY<-UB~VIfx^OZ*leTv758|WS-fy1ZEY9K05iJU{6#eUIH`k4bGa z@UY=llRP=Eghh+X$_z_EN5CQ zuL@KJkYOGhojZcOyb+|Nq|n-WzE}VEAOhl(^q^dI{9H}ae!Q(0Q9*$#M~`W53Y`TA z`nEuC1VQYOF}bGcR88PkYv0kEu&Aggv^48400000NkvXXu0mjfI`BS8 literal 0 HcmV?d00001 diff --git a/resources/images/icons/small/visible_w.png b/resources/images/icons/small/visible_w.png new file mode 100644 index 0000000000000000000000000000000000000000..8cef575e242ce414f0f73cb91de50e0b8bca1b2a GIT binary patch literal 1066 zcmV+_1l9YAP)MsTUoAAGS+fMm|G^)N)0z76>P>@P-?suCM1MK*!yzz z2XK5j?+388^+#NP-Ov48&wX9bInVu^1CyAM%sONYY+OeTt3&Ne3jAN}BNIq6z0oYKS2k4b75@$EqLIrKEUZJFo@tjpWb!JE^JJ z&yhn-baebeFc>5)?K%qX&SUk1E6B;o@|LOr%FXQ3xD7~32Wo-2q50i!zQ?PTd$@At z?}(-y$5_2)1paUwnQn?8LQx+phWf zt6bh~bC&Dk#owZJ%F+>CXIMNH_zXz(8ar|PH2#iGcf94vO)OlLAGU$nIXCml&ZpgR zfBygt^^IXXhVG5`CEX6-NOFL^z)g|ZEiJ8XoRARDs{5Bmq5&{>UM^W#SIe|Cw??B* z13r|L>;SpI!f4Fy?jAQzOG_a!F+QpRfSH*$x^Y)m#LgQ*Ca}^0zGvtvjcj$=R5!lV z^E-osifN#``>JSi^3)hi`hh0L%=&_d>OFO|x1Zze89)2#KBeToGQMv5#^o(5 zE@alMh)%(l6@-dRZ-f!#pdNG=cvuwmWSQ3A1$KI3|7>t18}^lQCS6=wEqXu|O_ z%m&pU=nZyPjv>{M<3cv$Hd(u6fBT9stU`;t>05m;qD+?u!ft2iaHGK-F6x z(AIW#6hA#Zl{IVcVZ+7}5)wwW>KS02nH?R|eFPIeNl!`Y_1>p`f2Y1Wc1n$39@mdQ z{1olV-6<(%FOJtpilmn%b;bMz8Ny{rRgz|or}l(?nV!Z07*qoM6N<$f_W|SeE