summaryrefslogtreecommitdiff
path: root/src/dic-options
diff options
context:
space:
mode:
Diffstat (limited to 'src/dic-options')
-rw-r--r--src/dic-options/DictionaryOptionsDialog.cpp62
-rw-r--r--src/dic-options/DictionaryOptionsDialog.h41
-rw-r--r--src/dic-options/DraggableListModel.cpp83
-rw-r--r--src/dic-options/DraggableListModel.h33
-rw-r--r--src/dic-options/FieldStyleDelegate.cpp35
-rw-r--r--src/dic-options/FieldStyleDelegate.h19
-rw-r--r--src/dic-options/FieldsListModel.cpp222
-rw-r--r--src/dic-options/FieldsListModel.h49
-rw-r--r--src/dic-options/FieldsPage.cpp139
-rw-r--r--src/dic-options/FieldsPage.h37
-rw-r--r--src/dic-options/FieldsPreviewModel.cpp34
-rw-r--r--src/dic-options/FieldsPreviewModel.h27
-rw-r--r--src/dic-options/FieldsView.cpp41
-rw-r--r--src/dic-options/FieldsView.h24
-rw-r--r--src/dic-options/PackFieldsListModel.cpp101
-rw-r--r--src/dic-options/PackFieldsListModel.h34
-rw-r--r--src/dic-options/PackFieldsView.cpp36
-rw-r--r--src/dic-options/PackFieldsView.h24
-rw-r--r--src/dic-options/PacksListModel.cpp81
-rw-r--r--src/dic-options/PacksListModel.h32
-rw-r--r--src/dic-options/PacksPage.cpp345
-rw-r--r--src/dic-options/PacksPage.h70
-rw-r--r--src/dic-options/UnusedFieldsListModel.cpp111
-rw-r--r--src/dic-options/UnusedFieldsListModel.h35
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