diff options
Diffstat (limited to 'src/dic-options')
24 files changed, 1715 insertions, 0 deletions
diff --git a/src/dic-options/DictionaryOptionsDialog.cpp b/src/dic-options/DictionaryOptionsDialog.cpp new file mode 100644 index 0000000..46bb245 --- /dev/null +++ b/src/dic-options/DictionaryOptionsDialog.cpp @@ -0,0 +1,62 @@ +#include "DictionaryOptionsDialog.h" +#include "../dictionary/CardPack.h" +#include "FieldsPage.h" +#include "PacksPage.h" +#include "FieldsListModel.h" + +DictionaryOptionsDialog::DictionaryOptionsDialog( Dictionary* aDict, QWidget* aParent ): + QDialog( aParent ), m_origDictPtr( aDict ) + { + initData(); + createPages(); + + QDialogButtonBox* okCancelBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal ); + connect( okCancelBox, SIGNAL(accepted()), this, SLOT(accept()) ); + connect( okCancelBox, SIGNAL(rejected()), this, SLOT(reject()) ); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(createDictPathLabel()); + mainLayout->addWidget( m_pages ); + mainLayout->addWidget( okCancelBox ); + setLayout( mainLayout ); + + setWindowTitle( tr("Dictionary options") ); + } + +DictionaryOptionsDialog::~DictionaryOptionsDialog() +{ + delete m_emptyField; +} + +QSize DictionaryOptionsDialog::sizeHint() const +{ + return QSize(700, 400); +} + +QLabel* DictionaryOptionsDialog::createDictPathLabel() +{ + const int MaxPathLength = 500; + QLabel* dictPathLabel = new QLabel; + QFontMetrics metrics(dictPathLabel->font()); + QString dictPath = QDir::toNativeSeparators(m_origDictPtr->getFilePath()); + dictPath = metrics.elidedText(dictPath, Qt::ElideMiddle, MaxPathLength); + dictPathLabel->setText("<b>" + tr("File name") + "</b>: " + dictPath); + return dictPathLabel; +} + +void DictionaryOptionsDialog::initData() + { + m_dict.setDictConfig( m_origDictPtr ); + m_emptyField = new Field(); + m_fieldsListModel = new FieldsListModel( this ); + } + +void DictionaryOptionsDialog::createPages() + { + m_pages = new QTabWidget; + m_pages->addTab( new FieldsPage( this ), QIcon(":/images/fields.png"), tr("Fields") ); + m_pages->addTab( new PacksPage( this ), QIcon(":/images/word-drill.png"), tr("Card packs") ); + } + + diff --git a/src/dic-options/DictionaryOptionsDialog.h b/src/dic-options/DictionaryOptionsDialog.h new file mode 100644 index 0000000..3a57cb6 --- /dev/null +++ b/src/dic-options/DictionaryOptionsDialog.h @@ -0,0 +1,41 @@ +#ifndef DICTIONARYOPTIONSDIALOG_H +#define DICTIONARYOPTIONSDIALOG_H + +#include <QtCore> +#include <QtWidgets> + +#include "../dictionary/Field.h" +#include "../dictionary/CardPack.h" +#include "../dictionary/Dictionary.h" + +class Dictionary; +class FieldsListModel; + +class DictionaryOptionsDialog : public QDialog +{ + Q_OBJECT + +public: + DictionaryOptionsDialog( Dictionary* aDict, QWidget* aParent ); + ~DictionaryOptionsDialog(); + QSize sizeHint() const; + +private: + void initData(); + void createPages(); + QLabel* createDictPathLabel(); + +public: + FieldsListModel* m_fieldsListModel; + Field* m_emptyField; + Dictionary m_dict; + +private: + Dictionary* m_origDictPtr; + QListWidget* m_contentsWidget; + QTabWidget* m_pages; +}; + +Q_DECLARE_METATYPE(QList<QPersistentModelIndex>) + +#endif // DICTIONARYOPTIONSDIALOG_H diff --git a/src/dic-options/DraggableListModel.cpp b/src/dic-options/DraggableListModel.cpp new file mode 100644 index 0000000..2ea0523 --- /dev/null +++ b/src/dic-options/DraggableListModel.cpp @@ -0,0 +1,83 @@ +#include "DraggableListModel.h" +#include "../dictionary/CardPack.h" +#include "../dictionary/Field.h" + +#include <QMimeData> + +Qt::ItemFlags DraggableListModel::flags(const QModelIndex &index) const + { + Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); + if (index.isValid()) + return Qt::ItemIsDragEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; + } + +Qt::DropActions DraggableListModel::supportedDropActions() const + { + return Qt::MoveAction; + } + +QStringList DraggableListModel::mimeTypes() const + { + QStringList types; + types << "application/octet-stream"; + return types; + } + +QMimeData *DraggableListModel::mimeData(const QModelIndexList &indexes) const + { + QStringList list; + QModelIndexList validIndexes; + foreach (QModelIndex index, indexes) + if (index.isValid() && index.column() == 0) + validIndexes << index; + qSort(validIndexes); + + int num = validIndexes.size(); + QByteArray encodedData; + QDataStream stream( &encodedData, QIODevice::WriteOnly ); + const void** ptrs = new const void*[num]; + int i=0; + foreach (QModelIndex index, validIndexes) + ptrs[i++] = dataPtr( index ); + stream.writeBytes( (char*)ptrs, num*sizeof(void*) ); + delete ptrs; + QMimeData *mimeData = new QMimeData(); + mimeData->setData( "application/octet-stream", encodedData ); + return mimeData; + } + +bool DraggableListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int /*column*/, const QModelIndex &parent) + { + if (action == Qt::IgnoreAction) + return true; + if (!data->hasFormat("application/octet-stream")) + return false; + + int beginRow; + if (row != -1) + beginRow = row; + else if (parent.isValid()) + beginRow = parent.row(); + else + beginRow = rowCount(QModelIndex()); + + QByteArray encodedData = data->data("application/octet-stream"); + QDataStream stream( &encodedData, QIODevice::ReadOnly ); + void** ptrs = new void*[ rowCount() ]; + uint num; + stream.readBytes( (char*&)ptrs, num ); //TODO: Avoid converting pointer to other types + num /= sizeof(void*); + + QList<QPersistentModelIndex> movedIndexes; + for(uint i=0; i<num; i++) + { + insertPointer( beginRow + i, ptrs[i] ); + movedIndexes << QPersistentModelIndex( index(beginRow + i, 0, QModelIndex()) ); + } + delete ptrs; + emit indexesDropped( movedIndexes ); + return true; + } diff --git a/src/dic-options/DraggableListModel.h b/src/dic-options/DraggableListModel.h new file mode 100644 index 0000000..e323def --- /dev/null +++ b/src/dic-options/DraggableListModel.h @@ -0,0 +1,33 @@ +#ifndef DRAGGABLELISTMODEL_H +#define DRAGGABLELISTMODEL_H + +#include <QAbstractListModel> + +#include "DictionaryOptionsDialog.h" +#include "../dictionary/Dictionary.h" + +class DraggableListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + DraggableListModel( DictionaryOptionsDialog* aParent ): + QAbstractListModel( aParent ), m_parent( aParent ) + {} + + Qt::ItemFlags flags(const QModelIndex &index) const; + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData * mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + virtual const void* dataPtr( const QModelIndex &aIndex ) const = 0; + virtual void insertPointer(int aPos, void *aData) = 0; + +signals: + void indexesDropped(QList<QPersistentModelIndex> aIndexes); + +protected: + DictionaryOptionsDialog* m_parent; +}; + +#endif diff --git a/src/dic-options/FieldStyleDelegate.cpp b/src/dic-options/FieldStyleDelegate.cpp new file mode 100644 index 0000000..6efee8c --- /dev/null +++ b/src/dic-options/FieldStyleDelegate.cpp @@ -0,0 +1,35 @@ +#include "FieldStyleDelegate.h" +#include "../dictionary/Field.h" +#include "../field-styles/FieldStyleFactory.h" + +#include <QComboBox> + +FieldStyleDelegate::FieldStyleDelegate(QObject *parent) : + QStyledItemDelegate(parent) + { + } + +QWidget* FieldStyleDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const + { + QComboBox* comboBox = new QComboBox( parent ); + comboBox->insertItems( 0, FieldStyleFactory::inst()->getStyleNames() ); + return comboBox; + } + +void FieldStyleDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const + { + QString value = index.model()->data(index, Qt::EditRole).toString(); + int cbIndex = FieldStyleFactory::inst()->getStyleNames().indexOf( value ); + QComboBox* comboBox = static_cast<QComboBox*>(editor); + comboBox->setCurrentIndex( cbIndex ); + } + +void FieldStyleDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const + { + QComboBox* comboBox = static_cast<QComboBox*>(editor); + QString value = comboBox->currentText(); + model->setData(index, value, Qt::EditRole); + } + diff --git a/src/dic-options/FieldStyleDelegate.h b/src/dic-options/FieldStyleDelegate.h new file mode 100644 index 0000000..2d969f5 --- /dev/null +++ b/src/dic-options/FieldStyleDelegate.h @@ -0,0 +1,19 @@ +#ifndef FIELDSTYLEDELEGATE_H +#define FIELDSTYLEDELEGATE_H + +#include <QStyledItemDelegate> + +class FieldStyleDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + FieldStyleDelegate(QObject *parent = 0); + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; +}; + + +#endif // FIELDSTYLEDELEGATE_H diff --git a/src/dic-options/FieldsListModel.cpp b/src/dic-options/FieldsListModel.cpp new file mode 100644 index 0000000..9fd23ac --- /dev/null +++ b/src/dic-options/FieldsListModel.cpp @@ -0,0 +1,222 @@ +#include "FieldsListModel.h" +#include "../dictionary/Field.h" + +#include <QMimeData> + +QVariant FieldsListModel::data( const QModelIndex &index, int role ) const + { + if (!index.isValid()) + return QVariant(); + if (index.row() >= rowCount()) + return QVariant(); + + const Field* field = m_parent->m_dict.field( index.row() ); + switch( role ) + { + case Qt::DisplayRole: + switch( index.column() ) + { + case 0: + return field->name(); + case 1: + return field->style(); + default: + return QVariant(); + } + case Qt::EditRole: + switch( index.column() ) + { + case 0: + return field->name(); + case 1: + return field->style(); + default: + return QVariant(); + } + case EStyleRole: + return field->style(); + default: + return QVariant(); + } + } + +QVariant FieldsListModel::headerData(int section, Qt::Orientation orientation, + int role) const + { + if( role != Qt::DisplayRole ) + return QVariant(); + if( orientation == Qt::Horizontal ) + switch( section ) + { + case 0: + return tr("Field"); + case 1: + return tr("Style"); + default: + return QVariant(); + } + else + return QVariant(); + } + +Qt::ItemFlags FieldsListModel::flags(const QModelIndex &index) const + { + Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); + if (index.isValid()) + return Qt::ItemIsEditable | Qt::ItemIsDragEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; + } + +bool FieldsListModel::setData(const QModelIndex &index, const QVariant &value, int role) + { + if( !index.isValid() || role!=Qt::EditRole ) + return false; + switch( index.column() ) + { + case 0: + m_parent->m_dict.setFieldName(index.row(), value.toString()); + emit dataChanged(index, index); + return true; + case 1: + m_parent->m_dict.setFieldStyle(index.row(), value.toString()); + emit dataChanged(index, index); + return true; + default: + return true; + } + } + +bool FieldsListModel::insertRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginInsertRows(QModelIndex(), position, position+rows-1); + for (int row = 0; row < rows; ++row) + m_parent->m_dict.insertField( position, tr("new field") ); + endInsertRows(); + return true; + } + +bool FieldsListModel::removeRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginRemoveRows(QModelIndex(), position, position+rows-1); + for (int row = 0; row < rows; ++row) + m_parent->m_dict.removeField( position ); + endRemoveRows(); + return true; + } + +void FieldsListModel::removeField(int aPos ) + { + beginRemoveRows(QModelIndex(), aPos, aPos); + m_parent->m_dict.destroyField( aPos ); + endRemoveRows(); + } + +void FieldsListModel::insertField(int aPos, Field* aField ) + { + beginInsertRows(QModelIndex(), aPos, aPos); + m_parent->m_dict.insertField( aPos, aField ); + endInsertRows(); + } + +Qt::DropActions FieldsListModel::supportedDropActions() const + { + return Qt::MoveAction; + } + +QStringList FieldsListModel::mimeTypes() const + { + QStringList types; + types << "application/octet-stream"; + return types; + } + +QMimeData *FieldsListModel::mimeData(const QModelIndexList &indexes) const + { + QStringList list; + QModelIndexList validIndexes; + foreach (QModelIndex index, indexes) + if (index.isValid() && index.column() == 0) + validIndexes << index; + qSort(validIndexes); + + int num = validIndexes.size(); + QByteArray encodedData; + QDataStream stream( &encodedData, QIODevice::WriteOnly ); + const Field** fieldPtrs = new const Field*[num]; + int i=0; + foreach (QModelIndex index, validIndexes) + fieldPtrs[i++] = m_parent->m_dict.field( index.row() ); + stream.writeBytes( (char*)fieldPtrs, num*sizeof(Field*) ); + delete fieldPtrs; + QMimeData *mimeData = new QMimeData(); + mimeData->setData( "application/octet-stream", encodedData ); + return mimeData; + } + +bool FieldsListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) + { + if (action == Qt::IgnoreAction) + return true; + if (!data->hasFormat("application/octet-stream")) + return false; + if (column > 0) + column = 0; + + int beginRow; + if (row != -1) + beginRow = row; + else if (parent.isValid()) + beginRow = parent.row(); + else + beginRow = rowCount(QModelIndex()); + + QByteArray encodedData = data->data("application/octet-stream"); + QDataStream stream( &encodedData, QIODevice::ReadOnly ); + Field** fieldPtrs = new Field*[ m_parent->m_dict.fieldsNum() ]; + uint num; + stream.readBytes( (char*&)fieldPtrs, num ); //TODO: Avoid converting pointer to other types + num /= sizeof(Field*); + + QList<QPersistentModelIndex> movedIndexes; + for(uint i=0; i<num; i++) + { + insertField( beginRow + i, fieldPtrs[i] ); + movedIndexes << QPersistentModelIndex( index(beginRow + i, 0, QModelIndex()) ); + } + delete fieldPtrs; + emit indexesDropped( movedIndexes ); + return true; + } + +void FieldsListModel::moveIndexesUpDown( QModelIndexList aIndexes, int aDirection ) + { + QList<QPersistentModelIndex> pSrcIndexes; + foreach (QModelIndex idx, aIndexes) + if( idx.isValid() && idx.column() == 0 ) + pSrcIndexes << QPersistentModelIndex( idx ); + + int rowsNum = pSrcIndexes.size(); + if( rowsNum == 0 ) + return; + + qSort( pSrcIndexes ); + + int beginRow; + if( aDirection < 0 ) + beginRow = pSrcIndexes[0].row()-1; + else + beginRow = pSrcIndexes[rowsNum-1].row()+2; + if( rowsNum == 1 && (beginRow < 0 || beginRow > rowCount()) ) + return; + if( beginRow < 0 ) + beginRow = 0; + if( beginRow > rowCount() ) + beginRow = rowCount(); + + QMimeData* mime = mimeData( aIndexes ); + dropMimeData( mime, Qt::MoveAction, beginRow, 0, QModelIndex() ); + foreach (QModelIndex index, pSrcIndexes) + removeRow( index.row() ); + } diff --git a/src/dic-options/FieldsListModel.h b/src/dic-options/FieldsListModel.h new file mode 100644 index 0000000..1cc4534 --- /dev/null +++ b/src/dic-options/FieldsListModel.h @@ -0,0 +1,49 @@ +#ifndef FIELDSLISTMODEL_H +#define FIELDSLISTMODEL_H + +#include <QAbstractTableModel> + +#include "DictionaryOptionsDialog.h" +#include "../dictionary/Dictionary.h" + +class FieldsListModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum + { + EStyleRole = Qt::UserRole + }; + + FieldsListModel( DictionaryOptionsDialog* aParent ): + QAbstractTableModel( aParent ), m_parent( aParent ) + {} + + int rowCount( const QModelIndex& /*parent*/ = QModelIndex() ) const + { return m_parent->m_dict.fieldsNum(); } + int columnCount( const QModelIndex& /*parent*/ = QModelIndex() ) const + { return 2; } + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + bool insertRows(int position, int rows, const QModelIndex &/*parent*/); + bool removeRows(int position, int rows, const QModelIndex &/*parent*/); + void insertField(int aPos, Field *aField); + void removeField( int aPos ); + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData* mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + void moveIndexesUpDown(QModelIndexList aIndexes, int aDirection); + +signals: + void indexesDropped(QList<QPersistentModelIndex> aIndexes); + +private: + DictionaryOptionsDialog* m_parent; +}; + +#endif // FIELDSLISTMODEL_H diff --git a/src/dic-options/FieldsPage.cpp b/src/dic-options/FieldsPage.cpp new file mode 100644 index 0000000..ea9a741 --- /dev/null +++ b/src/dic-options/FieldsPage.cpp @@ -0,0 +1,139 @@ +#include "FieldsPage.h" +#include "../dictionary/Dictionary.h" +#include "../dictionary/CardPack.h" +#include "FieldsListModel.h" +#include "FieldsPreviewModel.h" + +#include <QLabel> +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QPushButton> +#include <QToolButton> +#include <QGroupBox> + +FieldsPage::FieldsPage( DictionaryOptionsDialog* aParent ): + QWidget( aParent ), m_parent( aParent ) + { + createFieldsList(); + createFieldsPreview(); + + QHBoxLayout* mainLt = new QHBoxLayout; + mainLt->addLayout( m_fieldsLt ); + mainLt->addWidget( m_previewGroup ); + setLayout( mainLt ); + } + +void FieldsPage::createFieldsList() + { + QGroupBox* fieldsGroup = new QGroupBox( tr("Fields") ); + + QToolButton* moveUpBtn = new QToolButton; + moveUpBtn->setIcon(QIcon(":/images/1uparrow.png")); + moveUpBtn->setObjectName("up"); + moveUpBtn->setToolTip(tr("Move up")); + QToolButton* moveDownBtn = new QToolButton; + moveDownBtn->setIcon(QIcon(":/images/1downarrow.png")); + moveDownBtn->setObjectName("down"); + moveDownBtn->setToolTip(tr("Move down")); + QVBoxLayout* upDownLt = new QVBoxLayout; + upDownLt->addWidget( moveUpBtn ); + upDownLt->addWidget( moveDownBtn ); + upDownLt->addStretch( 1 ); + + connect( moveUpBtn, SIGNAL(clicked()), this, SLOT(moveItemsUpDown()) ); + connect( moveDownBtn, SIGNAL(clicked()), this, SLOT(moveItemsUpDown()) ); + + m_fieldsListView = new FieldsView; + m_fieldsListView->setModel( m_parent->m_fieldsListModel ); + QHBoxLayout* listHLt = new QHBoxLayout; + listHLt->addWidget( m_fieldsListView ); + listHLt->addLayout( upDownLt ); + fieldsGroup->setLayout( listHLt ); + + QPushButton* addBtn = new QPushButton( QPixmap(":/images/add.png"), tr("Add") ); + addBtn->setToolTip(tr("Add new field")); + QPushButton* removeBtn = new QPushButton( QPixmap(":/images/delete.png"), tr("Remove") ); + removeBtn->setToolTip(tr("Remove field(s)")); + QPushButton* editBtn = new QPushButton( QPixmap(":/images/edit.png"), tr("Rename") ); + editBtn->setToolTip(tr("Rename field")); + QHBoxLayout* btnLt = new QHBoxLayout; + btnLt->addWidget( addBtn ); + btnLt->addWidget( removeBtn ); + btnLt->addWidget( editBtn ); + + connect( addBtn, SIGNAL(clicked()), this, SLOT(addRow()) ); + connect( removeBtn, SIGNAL(clicked()), this, SLOT(removeRows()) ); + connect( editBtn, SIGNAL(clicked()), this, SLOT(editField()) ); + + m_fieldsLt = new QVBoxLayout; + m_fieldsLt->addWidget( fieldsGroup ); + m_fieldsLt->addLayout( btnLt ); + } + +void FieldsPage::createFieldsPreview() + { + FieldsPreviewModel* fieldsPreviewModel = new FieldsPreviewModel( m_parent ); + m_fieldsPreview = new QListView; + m_fieldsPreview->setModel( fieldsPreviewModel ); + + connect( m_parent->m_fieldsListModel, + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + m_fieldsPreview, SLOT(reset()) ); + connect( m_parent->m_fieldsListModel, + SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + m_fieldsPreview, SLOT(reset()) ); + connect( m_parent->m_fieldsListModel, + SIGNAL(rowsInserted(const QModelIndex&, int, int)), + m_fieldsPreview, SLOT(reset()) ); + + QHBoxLayout* previewLt = new QHBoxLayout; + previewLt->addWidget( m_fieldsPreview ); + m_previewGroup = new QGroupBox(tr("Preview")); + m_previewGroup->setLayout( previewLt ); + } + +void FieldsPage::moveItemsUpDown() + { + int direction; + if( sender()->objectName() == "up" ) + direction = -1; + else + direction = 1; + QModelIndexList selectedIndexes = m_fieldsListView->selectionModel()->selectedIndexes(); + m_fieldsListView->model()->moveIndexesUpDown( selectedIndexes, direction ); + } + +void FieldsPage::addRow() + { + QModelIndexList selectedIndexes = m_fieldsListView->selectionModel()->selectedRows(); + int row; + if( selectedIndexes.size() > 0 ) + { + qSort(selectedIndexes); + row = selectedIndexes[selectedIndexes.size()-1].row() + 1; + } + else + row = 0; + m_fieldsListView->model()->insertRow( row, QModelIndex() ); + } + +void FieldsPage::removeRows() + { + QModelIndexList selectedIndexes = m_fieldsListView->selectionModel()->selectedRows(); + QList<QPersistentModelIndex> pIndexes; + foreach (QModelIndex idx, selectedIndexes) + if( idx.isValid() ) + pIndexes << QPersistentModelIndex( idx ); + if( pIndexes.size() == 0 ) + return; + foreach( QModelIndex idx, pIndexes ) + m_fieldsListView->model()->removeField( idx.row() ); + } + +void FieldsPage::editField() + { + QModelIndex idx = m_fieldsListView->currentIndex(); + if( !idx.isValid() ) + return; + m_fieldsListView->edit( idx ); + } diff --git a/src/dic-options/FieldsPage.h b/src/dic-options/FieldsPage.h new file mode 100644 index 0000000..3cfdf99 --- /dev/null +++ b/src/dic-options/FieldsPage.h @@ -0,0 +1,37 @@ +#ifndef FIELDSPAGE_H +#define FIELDSPAGE_H + +#include <QListView> + +#include "DictionaryOptionsDialog.h" +#include "FieldsView.h" + +class Dictionary; +class QGroupBox; +class QVBoxLayout; + +class FieldsPage : public QWidget +{ + Q_OBJECT +public: + FieldsPage( DictionaryOptionsDialog* aParent ); + +private slots: + void moveItemsUpDown(); + void addRow(); + void removeRows(); + void editField(); + +private: + void createFieldsList(); + void createFieldsPreview(); + +private: + DictionaryOptionsDialog* m_parent; + QVBoxLayout* m_fieldsLt; + QGroupBox* m_previewGroup; + FieldsView* m_fieldsListView; + QListView* m_fieldsPreview; +}; + +#endif diff --git a/src/dic-options/FieldsPreviewModel.cpp b/src/dic-options/FieldsPreviewModel.cpp new file mode 100644 index 0000000..e6ce1c6 --- /dev/null +++ b/src/dic-options/FieldsPreviewModel.cpp @@ -0,0 +1,34 @@ +#include "FieldsPreviewModel.h" +#include "../dictionary/Field.h" +#include "../field-styles/FieldStyle.h" +#include "../field-styles/FieldStyleFactory.h" + +#include <QBrush> + +QVariant FieldsPreviewModel::data( const QModelIndex &index, int role ) const + { + if (!index.isValid()) + return QVariant(); + if (index.row() >= rowCount()) + return QVariant(); + + Field* field = m_parent->m_dict.fields().value(index.row()); + const FieldStyle fieldStyle = FieldStyleFactory::inst()->getStyle(field->style()); + switch( role ) + { + case Qt::DisplayRole: + return fieldStyle.prefix + field->name() + fieldStyle.suffix; + case Qt::FontRole: + return fieldStyle.font; + case Qt::BackgroundRole: + return QBrush( FieldStyleFactory::inst()->cardBgColor ); + case Qt::ForegroundRole: + return fieldStyle.color; + case Qt::SizeHintRole: + return QSize(0, 50); + case Qt::TextAlignmentRole: + return Qt::AlignCenter; + default: + return QVariant(); + } + } diff --git a/src/dic-options/FieldsPreviewModel.h b/src/dic-options/FieldsPreviewModel.h new file mode 100644 index 0000000..38e1fe2 --- /dev/null +++ b/src/dic-options/FieldsPreviewModel.h @@ -0,0 +1,27 @@ +#ifndef FIELDSPREVIEWMODEL_H +#define FIELDSPREVIEWMODEL_H + +#include <QAbstractListModel> + +#include "DictionaryOptionsDialog.h" +#include "../dictionary/Dictionary.h" + +class FieldsPreviewModel : public QAbstractListModel +{ + Q_OBJECT + +public: + FieldsPreviewModel( DictionaryOptionsDialog* aParent ): + QAbstractListModel( aParent ), m_parent( aParent ) + {} + + int rowCount( const QModelIndex& /*parent*/ = QModelIndex() ) const + { return m_parent->m_dict.fieldsNum(); } + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; + Qt::ItemFlags flags(const QModelIndex &/*index*/) const {return Qt::NoItemFlags;} + +private: + DictionaryOptionsDialog* m_parent; +}; + +#endif // FIELDSPREVIEWMODEL_H diff --git a/src/dic-options/FieldsView.cpp b/src/dic-options/FieldsView.cpp new file mode 100644 index 0000000..65801c7 --- /dev/null +++ b/src/dic-options/FieldsView.cpp @@ -0,0 +1,41 @@ +#include "FieldsView.h" +#include "FieldStyleDelegate.h" + +#include <QHeaderView> + +FieldsView::FieldsView(QWidget *parent): + QTableView( parent ) + { + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropOverwriteMode( false ); + FieldStyleDelegate* delegate = new FieldStyleDelegate(this); + setItemDelegateForColumn( 1, delegate ); + + setShowGrid(false); + verticalHeader()->hide(); + setSelectionBehavior( QAbstractItemView::SelectRows ); + } + +void FieldsView::startDrag(Qt::DropActions supportedActions) + { + selectionModel()->select( selectionModel()->selection(), + QItemSelectionModel::Select | QItemSelectionModel::Rows ); + QAbstractItemView::startDrag( supportedActions ); + } + +void FieldsView::setModel( FieldsListModel* aModel ) + { + QTableView::setModel( aModel ); + qRegisterMetaType< QList<QPersistentModelIndex> >(); + connect( aModel, SIGNAL(indexesDropped(QList<QPersistentModelIndex>)), + this, SLOT(selectIndexes(QList<QPersistentModelIndex>)), Qt::QueuedConnection ); + } + +void FieldsView::selectIndexes( QList<QPersistentModelIndex> aIndexes ) + { + selectionModel()->clearSelection(); + foreach( QPersistentModelIndex pIndex, aIndexes ) + selectionModel()->select( pIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows ); + } diff --git a/src/dic-options/FieldsView.h b/src/dic-options/FieldsView.h new file mode 100644 index 0000000..6944474 --- /dev/null +++ b/src/dic-options/FieldsView.h @@ -0,0 +1,24 @@ +#ifndef FIELDSVIEW_H +#define FIELDSVIEW_H + +#include <QTableView> + +#include "FieldsListModel.h" + +class FieldsView : public QTableView +{ + Q_OBJECT +public: + FieldsView(QWidget *parent = 0); + + FieldsListModel* model() const + { return qobject_cast<FieldsListModel*>(QAbstractItemView::model()); } + + void startDrag(Qt::DropActions supportedActions); + void setModel(FieldsListModel *aModel); +public slots: + void selectIndexes(QList<QPersistentModelIndex> aIndexes); + +}; + +#endif // FIELDSVIEW_H diff --git a/src/dic-options/PackFieldsListModel.cpp b/src/dic-options/PackFieldsListModel.cpp new file mode 100644 index 0000000..eb6eb6f --- /dev/null +++ b/src/dic-options/PackFieldsListModel.cpp @@ -0,0 +1,101 @@ +#include "PackFieldsListModel.h" +#include "../dictionary/CardPack.h" +#include "../dictionary/Field.h" + +#include <QMimeData> + +int PackFieldsListModel::rowCount( const QModelIndex& /*parent*/ ) const + { + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + if( !pack ) + return 0; + return pack->getFields().size(); + } + +QVariant PackFieldsListModel::data( const QModelIndex &index, int role ) const + { + if (!index.isValid()) + return QVariant(); + if (index.row() >= rowCount()) + return QVariant(); + + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + if( !pack ) + return QVariant(); + const Field* field = pack->getFields().value(index.row()); + switch( role ) + { + case Qt::DisplayRole: + case Qt::EditRole: + return field->name(); + case Qt::FontRole: + if( index.row() == 0 ) + { + QFont font; + font.setBold(true); + return font; + } + else + return QFont(); + default: + return QVariant(); + } + } + +void PackFieldsListModel::changeParentRow( const QModelIndex& aIndex ) + { + m_parentRow = aIndex.row(); + emit layoutChanged(); + } + +bool PackFieldsListModel::setData(const QModelIndex& index, const QVariant& aValue, int role) + { + if( !index.isValid() || role != Qt::EditRole ) + return false; + const Field* field = m_parent->m_dict.field( aValue.toString() ); + if( !field ) + return false; + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + pack->setField( index.row(), field ); + emit dataChanged(index, index); + return true; + } + +bool PackFieldsListModel::insertRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginInsertRows(QModelIndex(), position, position+rows-1); + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + for (int row = 0; row < rows; ++row) + pack->insertField( position, m_parent->m_emptyField ); + endInsertRows(); + return true; + } + +bool PackFieldsListModel::removeRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginRemoveRows(QModelIndex(), position, position+rows-1); + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + for (int row = 0; row < rows; ++row) + pack->removeField( position ); + endRemoveRows(); + return true; + } + +const void* PackFieldsListModel::dataPtr( const QModelIndex& aIndex ) const + { + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + if( !pack ) + return NULL; + const Field* field = pack->getFields().value( aIndex.row() ); + return static_cast<const void*>( field ); + } + +void PackFieldsListModel::insertPointer( int aPos, void* aData ) + { + beginInsertRows(QModelIndex(), aPos, aPos ); + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + if( !pack ) + return; + pack->insertField( aPos, static_cast<Field*>( aData ) ); + endInsertRows(); + } diff --git a/src/dic-options/PackFieldsListModel.h b/src/dic-options/PackFieldsListModel.h new file mode 100644 index 0000000..06565c5 --- /dev/null +++ b/src/dic-options/PackFieldsListModel.h @@ -0,0 +1,34 @@ +#ifndef PACKFIELDSLISTMODEL_H +#define PACKFIELDSLISTMODEL_H + +#include <QAbstractListModel> + +#include "DictionaryOptionsDialog.h" +#include "DraggableListModel.h" +#include "../dictionary/Dictionary.h" + +class PackFieldsListModel : public DraggableListModel +{ + Q_OBJECT + +public: + PackFieldsListModel( DictionaryOptionsDialog* aParent ): + DraggableListModel( aParent ), m_parentRow( 0 ) + {} + + int rowCount( const QModelIndex& parent = QModelIndex() ) const; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; + bool setData(const QModelIndex& index, const QVariant& aValue, int role = Qt::EditRole ); + bool insertRows(int position, int rows, const QModelIndex &); + bool removeRows(int position, int rows, const QModelIndex &); + const void* dataPtr( const QModelIndex& aIndex ) const; + void insertPointer( int aPos, void* aData ); + +public slots: + void changeParentRow( const QModelIndex& aIndex ); + +private: + int m_parentRow; +}; + +#endif diff --git a/src/dic-options/PackFieldsView.cpp b/src/dic-options/PackFieldsView.cpp new file mode 100644 index 0000000..6925984 --- /dev/null +++ b/src/dic-options/PackFieldsView.cpp @@ -0,0 +1,36 @@ +#include "PackFieldsView.h" + +PackFieldsView::PackFieldsView(QWidget *parent): + QListView( parent ) + { + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropOverwriteMode( false ); + } + +void PackFieldsView::setModel( QAbstractItemModel* aModel ) + { + QListView::setModel( aModel ); + qRegisterMetaType< QList<QPersistentModelIndex> >(); + connect( aModel, SIGNAL(indexesDropped(QList<QPersistentModelIndex>)), + this, SLOT(selectIndexes(QList<QPersistentModelIndex>)), Qt::QueuedConnection ); + } + +void PackFieldsView::selectIndexes( QList<QPersistentModelIndex> aIndexes ) + { + selectionModel()->clearSelection(); + foreach( QPersistentModelIndex pIndex, aIndexes ) + { + QModelIndex index = model()->index( pIndex.row(), 0, pIndex.parent() ); + selectionModel()->select( index, QItemSelectionModel::Select ); + } + selectionModel()->setCurrentIndex( aIndexes[0], QItemSelectionModel::NoUpdate ); + } + +void PackFieldsView::updateCurrent() + { + QItemSelection selection = selectionModel()->selection(); + reset(); + selectionModel()->select( selection, QItemSelectionModel::Select ); + } diff --git a/src/dic-options/PackFieldsView.h b/src/dic-options/PackFieldsView.h new file mode 100644 index 0000000..7c4aeea --- /dev/null +++ b/src/dic-options/PackFieldsView.h @@ -0,0 +1,24 @@ +#ifndef PACKFIELDSVIEW_H +#define PACKFIELDSVIEW_H + +#include <QListView> + +#include "PackFieldsListModel.h" +#include "DraggableListModel.h" + +class PackFieldsView : public QListView +{ + Q_OBJECT +public: + PackFieldsView(QWidget *parent = 0); + + void setModel(QAbstractItemModel *aModel); + DraggableListModel* model() const + { return qobject_cast<DraggableListModel*>(QAbstractItemView::model()); } +public slots: + void selectIndexes(QList<QPersistentModelIndex> aIndexes); + void updateCurrent(); + +}; + +#endif diff --git a/src/dic-options/PacksListModel.cpp b/src/dic-options/PacksListModel.cpp new file mode 100644 index 0000000..c2dfc31 --- /dev/null +++ b/src/dic-options/PacksListModel.cpp @@ -0,0 +1,81 @@ +#include "PacksListModel.h" +#include "../dictionary/CardPack.h" + +QVariant PacksListModel::data( const QModelIndex &index, int role ) const + { + if (!index.isValid()) + return QVariant(); + if (index.row() >= rowCount()) + return QVariant(); + + switch( role ) + { + case Qt::DisplayRole: + case Qt::EditRole: + { + const CardPack* pack = m_parent->m_dict.cardPack( index.row() ); + if( !pack ) + return QVariant(); + return pack->id(); + } + default: + return QVariant(); + } + } + +bool PacksListModel::setData(const QModelIndex& index, const QVariant& aValue, int role) + { + if( !index.isValid() || role != Qt::EditRole ) + return false; + m_parent->m_dict.cardPack( index.row() )->setName( aValue.toString() ); + emit dataChanged(index, index); + return true; + } + +Qt::ItemFlags PacksListModel::flags(const QModelIndex &index) const + { + Qt::ItemFlags defaultFlags = DraggableListModel::flags(index); + return defaultFlags; + } + +bool PacksListModel::insertRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginInsertRows(QModelIndex(), position, position+rows-1); + for (int i = 0; i < rows; i++) + { + CardPack* newPack = new CardPack( &(m_parent->m_dict) ); + newPack->addField( m_parent->m_dict.field(0) ); + m_parent->m_dict.insertPack( position + i, newPack ); + } + endInsertRows(); + return true; + } + +bool PacksListModel::removeRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginRemoveRows(QModelIndex(), position, position+rows-1); + for (int row = 0; row < rows; ++row) + m_parent->m_dict.removePack( position ); + endRemoveRows(); + return true; + } + +const void* PacksListModel::dataPtr( const QModelIndex& aIndex ) const + { + CardPack* pack = m_parent->m_dict.cardPack( aIndex.row() ); + return static_cast<const void*>( pack ); + } + +void PacksListModel::insertPointer( int aPos, void* aData ) + { + beginInsertRows(QModelIndex(), aPos, aPos ); + m_parent->m_dict.insertPack( aPos, static_cast<CardPack*>( aData ) ); + endInsertRows(); + } + +void PacksListModel::removePack( int aPos ) + { + beginRemoveRows(QModelIndex(), aPos, aPos); + m_parent->m_dict.destroyPack( aPos ); + endRemoveRows(); + } diff --git a/src/dic-options/PacksListModel.h b/src/dic-options/PacksListModel.h new file mode 100644 index 0000000..3523912 --- /dev/null +++ b/src/dic-options/PacksListModel.h @@ -0,0 +1,32 @@ +#ifndef PACKSLISTMODEL_H +#define PACKSLISTMODEL_H + +#include <QAbstractListModel> + +#include "DictionaryOptionsDialog.h" +#include "DraggableListModel.h" +#include "../dictionary/Dictionary.h" + +class PacksListModel : public DraggableListModel +{ + Q_OBJECT + +public: + PacksListModel( DictionaryOptionsDialog* aParent ): + DraggableListModel( aParent ) + {} + + int rowCount( const QModelIndex& /*parent*/ = QModelIndex() ) const + { return m_parent->m_dict.cardPacksNum(); } + + QVariant data( const QModelIndex &index, int role ) const; + bool setData(const QModelIndex& index, const QVariant& aValue, int role = Qt::EditRole ); + Qt::ItemFlags flags(const QModelIndex &index) const; + bool insertRows(int position, int rows, const QModelIndex &); + bool removeRows(int position, int rows, const QModelIndex &); + const void* dataPtr( const QModelIndex& aIndex ) const; + void insertPointer( int aPos, void* aData ); + void removePack( int aPos ); +}; + +#endif diff --git a/src/dic-options/PacksPage.cpp b/src/dic-options/PacksPage.cpp new file mode 100644 index 0000000..e78bbf0 --- /dev/null +++ b/src/dic-options/PacksPage.cpp @@ -0,0 +1,345 @@ +#include "PacksPage.h" +#include "FieldsListModel.h" +#include "PacksListModel.h" +#include "PackFieldsListModel.h" +#include "PackFieldsView.h" +#include "UnusedFieldsListModel.h" +#include "../dictionary/Dictionary.h" +#include "../study/CardSideView.h" +#include "../dictionary/CardPack.h" +#include "../field-styles/FieldStyleFactory.h" + +#include <QLabel> +#include <QPushButton> +#include <QToolButton> +#include <QStringListModel> + +PacksPage::PacksPage( DictionaryOptionsDialog* aParent ): + QWidget( aParent ), m_parent( aParent ) + { + createPacksList(); + createPackFieldsList(); + createUnusedFieldsList(); + createPackPreview(); + + QGridLayout* mainLt = new QGridLayout; + mainLt->addLayout( m_packsListLt, 0, 0, 2, 1 ); + mainLt->addLayout( m_fieldsListLt, 0, 1 ); + mainLt->addLayout( m_unusedFieldsListLt, 1, 1 ); + mainLt->addLayout( m_previewLt, 0, 2, 2, 1 ); + setLayout( mainLt ); + + // Select first pack + m_packsListView->selectionModel()->setCurrentIndex( m_packFieldsListModel->index(0, 0), + QItemSelectionModel::Select ); + } + +PacksPage::~PacksPage() + { + } + +void PacksPage::createPacksList() + { + m_packsListModel = new PacksListModel( m_parent ); + m_packsListView = new PackFieldsView( this ); + m_packsListView->setModel( m_packsListModel ); + m_packsListView->setDropIndicatorShown(true); + m_packsListView->setMinimumWidth( 200 ); + + QPushButton* addPackBtn = new QPushButton( QPixmap(":/images/add.png"), tr("Add") ); + QPushButton* removePackBtn = new QPushButton( QPixmap(":/images/delete.png"), tr("Remove") ); + QHBoxLayout* packBtnLt = new QHBoxLayout; + packBtnLt->addWidget( addPackBtn ); + packBtnLt->addWidget( removePackBtn ); + + connect( addPackBtn, SIGNAL(clicked()), this, SLOT(addPack()) ); + connect( removePackBtn, SIGNAL(clicked()), this, SLOT(removePacks()) ); + + QToolButton* packMoveUpBtn = new QToolButton; + packMoveUpBtn->setObjectName("pack-up"); + packMoveUpBtn->setIcon(QIcon(":/images/1uparrow.png")); + packMoveUpBtn->setToolTip(tr("Move pack up")); + QToolButton* packMoveDownBtn = new QToolButton; + packMoveDownBtn->setObjectName("pack-down"); + packMoveDownBtn->setIcon(QIcon(":/images/1downarrow.png")); + packMoveDownBtn->setToolTip(tr("Move pack down")); + QVBoxLayout* packMoveBtnsLt = new QVBoxLayout; + packMoveBtnsLt->addWidget( packMoveUpBtn ); + packMoveBtnsLt->addWidget( packMoveDownBtn ); + packMoveBtnsLt->addStretch( 1 ); + + connect( packMoveUpBtn, SIGNAL(clicked()), this, SLOT(moveItemsUpDown()) ); + connect( packMoveDownBtn, SIGNAL(clicked()), this, SLOT(moveItemsUpDown()) ); + + m_packsListLt = new QGridLayout; + m_packsListLt->addWidget( new QLabel("<b>"+tr("Card packs")+"</b>"), 0, 0, 1, 2 ); + m_packsListLt->addWidget( m_packsListView, 1, 0 ); + m_packsListLt->addLayout( packMoveBtnsLt, 1, 1 ); + m_packsListLt->addLayout( packBtnLt, 2, 0, 1, 2 ); + } + +void PacksPage::createPackFieldsList() + { + m_packFieldsListModel = new PackFieldsListModel( m_parent ); + m_fieldsListView = new PackFieldsView( this ); + m_fieldsListView->setModel( m_packFieldsListModel ); + m_fieldsListView->setDropIndicatorShown(true); + + connect( m_packsListView->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + m_packFieldsListModel, SLOT(changeParentRow(const QModelIndex&)) ); + connect( m_packsListView->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updateUsesExactAnswer(const QModelIndex&)) ); + connect( m_packFieldsListModel, + SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + m_packsListView, SLOT(updateCurrent()) ); + connect( m_packFieldsListModel, + SIGNAL(rowsInserted(const QModelIndex&, int, int)), + m_packsListView, SLOT(updateCurrent()) ); + + QToolButton* fieldMoveUpBtn = new QToolButton; + fieldMoveUpBtn->setObjectName("field-up"); + fieldMoveUpBtn->setIcon(QIcon(":/images/1uparrow.png")); + fieldMoveUpBtn->setToolTip(tr("Move field up")); + QToolButton* fieldMoveDownBtn = new QToolButton; + fieldMoveDownBtn->setObjectName("field-down"); + fieldMoveDownBtn->setIcon(QIcon(":/images/1downarrow.png")); + fieldMoveDownBtn->setToolTip(tr("Move field down")); + QVBoxLayout* fieldBtnsLt = new QVBoxLayout; + fieldBtnsLt->addWidget( fieldMoveUpBtn ); + fieldBtnsLt->addWidget( fieldMoveDownBtn ); + fieldBtnsLt->addStretch( 1 ); + + connect( fieldMoveUpBtn, SIGNAL(clicked()), this, SLOT(moveItemsUpDown()) ); + connect( fieldMoveDownBtn, SIGNAL(clicked()), this, SLOT(moveItemsUpDown()) ); + + m_fieldsListLt = new QGridLayout; + m_fieldsListLt->addWidget( new QLabel("<b>"+tr("Pack fields")+"</b>"), 0, 0, 1, 2 ); + m_fieldsListLt->addWidget( m_fieldsListView, 1, 0 ); + m_fieldsListLt->addLayout( fieldBtnsLt, 1, 1 ); + + } + +void PacksPage::createUnusedFieldsList() + { + m_unusedFieldsListModel = new UnusedFieldsListModel( m_parent ); + m_unusedFieldsListView = new PackFieldsView( this ); + m_unusedFieldsListView->setModel( m_unusedFieldsListModel ); + + connect( m_packsListView->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + m_unusedFieldsListModel, SLOT(changeParentRow(const QModelIndex&)) ); + + connect( m_packFieldsListModel, + SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + m_unusedFieldsListModel, SLOT(updateUnusedFields()) ); + connect( m_packFieldsListModel, + SIGNAL(rowsInserted(const QModelIndex&, int, int)), + m_unusedFieldsListModel, SLOT(updateUnusedFields()) ); + connect( m_parent->m_fieldsListModel, + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + m_unusedFieldsListModel, SLOT(updateUnusedFields()) ); + connect( m_parent->m_fieldsListModel, + SIGNAL(rowsInserted(const QModelIndex&, int, int)), + m_unusedFieldsListModel, SLOT(updateUnusedFields()) ); + connect( m_parent->m_fieldsListModel, + SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + m_unusedFieldsListModel, SLOT(updateUnusedFields()) ); + + QToolButton* fieldRemoveBtn = new QToolButton; + fieldRemoveBtn->setIcon(QIcon(":/images/down.png")); + fieldRemoveBtn->setToolTip(tr("Remove field from pack")); + QToolButton* fieldAddBtn = new QToolButton; + fieldAddBtn->setIcon(QIcon(":/images/up.png")); + fieldAddBtn->setToolTip(tr("Add field to pack")); + QHBoxLayout* usedFieldsBtnsLt = new QHBoxLayout; + usedFieldsBtnsLt->addWidget( fieldRemoveBtn ); + usedFieldsBtnsLt->addWidget( fieldAddBtn ); + + connect( fieldRemoveBtn, SIGNAL(clicked()), this, SLOT(removeFields()) ); + connect( fieldAddBtn, SIGNAL(clicked()), this, SLOT(addFields()) ); + + usesExactAnswerBox = new QCheckBox(tr("Uses exact answer")); + connect(usesExactAnswerBox, + SIGNAL(stateChanged(int)), SLOT(updatePackUsesExactAnswer(int))); + + m_unusedFieldsListLt = new QGridLayout; + m_unusedFieldsListLt->addLayout( usedFieldsBtnsLt, 0, 0 ); + m_unusedFieldsListLt->addWidget( new QLabel("<b>"+tr("Unused fields")+"</b>"), 0, 1 ); + m_unusedFieldsListLt->addWidget( m_unusedFieldsListView, 1, 0, 1, 2 ); + m_unusedFieldsListLt->addWidget(usesExactAnswerBox, 2, 0, 1, 2); + } + +void PacksPage::createPackPreview() + { + m_qstPreview = new CardSideView( CardSideView::QstMode ); + m_qstPreview->setMinimumSize( 200, 70 ); + m_ansPreview = new CardSideView( CardSideView::AnsMode ); + m_ansPreview->setMinimumSize( 200, 70 ); + m_previewLt = new QVBoxLayout; + m_previewLt->addWidget( new QLabel( "<b>"+tr("Preview")+"</b>") ); + m_previewLt->addWidget( m_qstPreview, 1 ); + m_previewLt->addWidget( m_ansPreview, 1 ); + + // From: the packs list + connect( m_packsListView->selectionModel(), + SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updatePreviewForPack()) ); + + // From: the pack fields list + connect( m_packFieldsListModel, + SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + SLOT(updatePreviewForPack()) ); + connect( m_packFieldsListModel, + SIGNAL(rowsInserted(const QModelIndex&, int, int)), + SLOT(updatePreviewForPack()) ); + + // From: the dictionary field list at the "Fields" page + connect( m_parent->m_fieldsListModel, + SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), + SLOT(updatePreviewForPack()) ); + connect( m_parent->m_fieldsListModel, + SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + SLOT(updatePreviewForPack()) ); + } + +void PacksPage::updatePreviewForPack() + { + int selectedPack = m_packsListView->selectionModel()->currentIndex().row(); + if( selectedPack > -1 ) // If any selection + m_curPack = selectedPack; + CardPack* pack = m_parent->m_dict.cardPack( m_curPack ); + if( !pack ) + return; + + const Field* qstField = pack->getQuestionField(); + if( !qstField ) + return; + QStringList ansList; + foreach( const Field* field, pack->getAnswerFields() ) + ansList << field->name(); + m_qstPreview->setPack( pack ); + m_qstPreview->setQuestion( qstField->name() ); + m_ansPreview->setPack( pack ); + m_ansPreview->setQstAnsr( qstField->name(), ansList ); + } + +void PacksPage::moveItemsUpDown() + { + PackFieldsView* view; + if( sender()->objectName().contains("pack") ) + view = m_packsListView; + else if( sender()->objectName().contains("field") ) + view = m_fieldsListView; + else + return; + DraggableListModel* model = view->model(); + + QModelIndexList selectedIndexes = view->selectionModel()->selectedIndexes(); + QList<QPersistentModelIndex> pSrcIndexes; + foreach (QModelIndex idx, selectedIndexes) + if( idx.isValid() ) + pSrcIndexes << QPersistentModelIndex( idx ); + int rowsNum = pSrcIndexes.size(); + if( rowsNum == 0 ) + return; + qSort( pSrcIndexes ); + + int beginRow; + if( sender()->objectName().contains("up") ) + beginRow = pSrcIndexes[0].row()-1; + else if( sender()->objectName().contains("down") ) + beginRow = pSrcIndexes[rowsNum-1].row()+2; + else + return; + if( rowsNum == 1 && (beginRow < 0 || beginRow > model->rowCount()) ) + return; + if( beginRow < 0 ) + beginRow = 0; + if( beginRow > model->rowCount() ) + beginRow = model->rowCount(); + + QMimeData* mime = model->mimeData( selectedIndexes ); + model->dropMimeData( mime, Qt::MoveAction, beginRow, 0, QModelIndex() ); + foreach (QModelIndex index, pSrcIndexes) + model->removeRow( index.row() ); + } + +void PacksPage::removeFields() + { + if( m_packFieldsListModel->rowCount() <= 1 ) + return; + QModelIndexList selectedIndexes = m_fieldsListView->selectionModel()->selectedIndexes(); + if( selectedIndexes.isEmpty() ) + return; + QList<QPersistentModelIndex> pSrcIndexes; + foreach (QModelIndex idx, selectedIndexes) + if( idx.isValid() ) + pSrcIndexes << QPersistentModelIndex( idx ); + foreach (QModelIndex index, pSrcIndexes) + m_fieldsListView->model()->removeRow( index.row() ); + } + +void PacksPage::addFields() + { + QModelIndexList selectedIndexes = m_unusedFieldsListView->selectionModel()->selectedIndexes(); + if( selectedIndexes.isEmpty() ) + return; + int dstRow = m_fieldsListView->currentIndex().row()+1; + QMimeData* mime = m_unusedFieldsListModel->mimeData( selectedIndexes ); + m_packFieldsListModel->dropMimeData( mime, Qt::MoveAction, dstRow, 0, QModelIndex() ); + } + + +void PacksPage::addPack() + { + QModelIndexList selectedIndexes = m_packsListView->selectionModel()->selectedRows(); + int row; + if( selectedIndexes.size() > 0 ) + { + qSort(selectedIndexes); + row = selectedIndexes[selectedIndexes.size()-1].row() + 1; + } + else + row = 0; + m_packsListModel->insertRow( row, QModelIndex() ); + } + +void PacksPage::removePacks() + { + QModelIndexList selectedIndexes = m_packsListView->selectionModel()->selectedRows(); + QList<QPersistentModelIndex> pIndexes; + foreach (QModelIndex idx, selectedIndexes) + if( idx.isValid() ) + pIndexes << QPersistentModelIndex( idx ); + if( pIndexes.size() == 0 ) + return; + foreach( QModelIndex idx, pIndexes ) + m_packsListModel->removePack( idx.row() ); + } + +void PacksPage::renamePack() + { + QModelIndex idx = m_packsListView->currentIndex(); + if( !idx.isValid() ) + return; + m_packsListView->edit( idx ); + } + +void PacksPage::updateUsesExactAnswer(const QModelIndex& index) +{ + int selectedPack = index.row(); + if( selectedPack > -1 ) // If any selection + m_curPack = selectedPack; + CardPack* pack = m_parent->m_dict.cardPack( m_curPack ); + if( !pack ) + return; + usesExactAnswerBox->setChecked(pack->getUsesExactAnswer()); +} + +void PacksPage::updatePackUsesExactAnswer(int state) +{ + CardPack* pack = m_parent->m_dict.cardPack( m_curPack ); + pack->setUsesExactAnswer(state == Qt::Checked); +} diff --git a/src/dic-options/PacksPage.h b/src/dic-options/PacksPage.h new file mode 100644 index 0000000..e191367 --- /dev/null +++ b/src/dic-options/PacksPage.h @@ -0,0 +1,70 @@ +#ifndef PACKSPAGE_H +#define PACKSPAGE_H + +#include <QWidget> +#include <QAbstractListModel> +#include <QModelIndex> +#include <QListView> +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QGridLayout> + +#include "DictionaryOptionsDialog.h" +#include "PackFieldsView.h" + +class CardSideView; +class Dictionary; +class PacksListModel; +class UnusedFieldsListModel; + +class PacksPage : public QWidget +{ + Q_OBJECT +public: + PacksPage( DictionaryOptionsDialog* aParent ); + ~PacksPage(); + +public slots: + void moveItemsUpDown(); + void removeFields(); + void addFields(); + void addPack(); + void removePacks(); + void renamePack(); + +private: + void createPacksList(); + void createPackFieldsList(); + void createUnusedFieldsList(); + void createPackPreview(); + +private slots: + void updatePreviewForPack(); + void updateUsesExactAnswer(const QModelIndex& index); + void updatePackUsesExactAnswer(int state); + +private: + DictionaryOptionsDialog* m_parent; + + // Models + PacksListModel* m_packsListModel; + QAbstractListModel* m_packFieldsListModel; + UnusedFieldsListModel* m_unusedFieldsListModel; + int m_curPack; + + // List views + PackFieldsView* m_packsListView; + PackFieldsView* m_fieldsListView; + PackFieldsView* m_unusedFieldsListView; + QCheckBox* usesExactAnswerBox; + CardSideView* m_qstPreview; + CardSideView* m_ansPreview; + + // Layouts + QGridLayout* m_packsListLt; + QGridLayout* m_fieldsListLt; + QGridLayout* m_unusedFieldsListLt; + QVBoxLayout* m_previewLt; +}; + +#endif diff --git a/src/dic-options/UnusedFieldsListModel.cpp b/src/dic-options/UnusedFieldsListModel.cpp new file mode 100644 index 0000000..3311285 --- /dev/null +++ b/src/dic-options/UnusedFieldsListModel.cpp @@ -0,0 +1,111 @@ +#include "UnusedFieldsListModel.h" +#include "../dictionary/CardPack.h" +#include "../dictionary/Dictionary.h" + +#include <QMimeData> + +UnusedFieldsListModel::UnusedFieldsListModel( DictionaryOptionsDialog* aParent ): + DraggableListModel( aParent ), m_parentRow( 0 ) + { + updateUnusedFields(); + } + + +int UnusedFieldsListModel::rowCount(const QModelIndex& /*parent*/) const + { + return m_unusedFields.size(); + } + +QVariant UnusedFieldsListModel::data( const QModelIndex &index, int role ) const + { + if (!index.isValid()) + return QVariant(); + if (index.row() >= rowCount()) + return QVariant(); + + switch( role ) + { + case Qt::DisplayRole: + case Qt::EditRole: + return m_unusedFields.value(index.row())->name(); + default: + return QVariant(); + } + } + +void UnusedFieldsListModel::updateUnusedFields() + { + beginResetModel(); + m_unusedFields.clear(); + CardPack* pack = m_parent->m_dict.cardPack( m_parentRow ); + if( !pack ) + return; + QList<const Field*> packFields = pack->getFields(); + for( int i=0; i < m_parent->m_dict.fieldsNum(); i++ ) + { + const Field* dictField = m_parent->m_dict.field( i ); + if( !packFields.contains( dictField ) ) + m_unusedFields << dictField; + } + endResetModel(); + } + +void UnusedFieldsListModel::changeParentRow( const QModelIndex& aIndex ) + { + m_parentRow = aIndex.row(); + updateUnusedFields(); + emit layoutChanged(); + } + +bool UnusedFieldsListModel::setData(const QModelIndex& index, const QVariant& aValue, int role) + { + if( !index.isValid() || role != Qt::EditRole ) + return false; + const Field* field = m_parent->m_dict.field( aValue.toString() ); + if( !field ) + return false; + m_unusedFields[index.row()] = field; + emit dataChanged(index, index); + return true; + } + +bool UnusedFieldsListModel::insertRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginInsertRows(QModelIndex(), position, position+rows-1); + for (int row = 0; row < rows; ++row) + m_unusedFields.insert( position, m_parent->m_emptyField ); + endInsertRows(); + updateUnusedFields(); + return true; + } + +bool UnusedFieldsListModel::removeRows(int position, int rows, const QModelIndex &/*parent*/) + { + beginRemoveRows(QModelIndex(), position, position+rows-1); + for (int row = 0; row < rows; ++row) + m_unusedFields.removeAt( position ); + endRemoveRows(); + updateUnusedFields(); + return true; + } + +const void* UnusedFieldsListModel::dataPtr( const QModelIndex& aIndex ) const + { + const Field* field = m_unusedFields.value( aIndex.row() ); + return static_cast<const void*>( field ); + } + +void UnusedFieldsListModel::insertPointer( int aPos, void* aData ) + { + m_unusedFields.insert( aPos, static_cast<Field*>( aData ) ); + } + +bool UnusedFieldsListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int /*row*/, int /*column*/, const QModelIndex &/*parent*/) + { + if (action == Qt::IgnoreAction) + return true; + if (!data->hasFormat("application/octet-stream")) + return false; + return true; + } diff --git a/src/dic-options/UnusedFieldsListModel.h b/src/dic-options/UnusedFieldsListModel.h new file mode 100644 index 0000000..c3a95f6 --- /dev/null +++ b/src/dic-options/UnusedFieldsListModel.h @@ -0,0 +1,35 @@ +#ifndef UNUSEDFIELDSLISTMODEL_H +#define UNUSEDFIELDSLISTMODEL_H + +#include <QAbstractListModel> + +#include "DictionaryOptionsDialog.h" +#include "DraggableListModel.h" + +class UnusedFieldsListModel : public DraggableListModel +{ + Q_OBJECT + +public: + UnusedFieldsListModel( DictionaryOptionsDialog* aParent ); + + int rowCount( const QModelIndex& /*parent*/ = QModelIndex() ) const; + QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const; + bool setData(const QModelIndex& index, const QVariant& aValue, int role = Qt::EditRole ); + bool insertRows(int position, int rows, const QModelIndex &); + bool removeRows(int position, int rows, const QModelIndex &); + const void* dataPtr( const QModelIndex& aIndex ) const; + void insertPointer( int aPos, void* aData ); + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent); + +public slots: + void changeParentRow( const QModelIndex& aIndex ); + void updateUnusedFields(); + +private: + int m_parentRow; + QList<const Field*> m_unusedFields; ///< Always updated list +}; + +#endif |