From 45b97b8f97ed41a8ba127742f1cc417673f95e11 Mon Sep 17 00:00:00 2001 From: Diane Trout Date: Fri, 29 Sep 2006 01:01:06 +0000 Subject: [PATCH] better motif editor ticket:122 This patch uses a QTableView for the motif editor which provides both a better looking editor and supports adding an arbitrary number of motifs. --- qui/CMakeLists.txt | 7 +- qui/MussaWindow.cpp | 1 - qui/motif_editor/MotifDetail.cpp | 105 ----------- qui/motif_editor/MotifDetail.hpp | 52 ------ qui/motif_editor/MotifEditor.cpp | 84 ++++++--- qui/motif_editor/MotifEditor.hpp | 27 ++- qui/motif_editor/MotifEditorDelegate.cpp | 182 ++++++++++++++++++ qui/motif_editor/MotifEditorDelegate.hpp | 34 ++++ qui/motif_editor/MotifElement.cpp | 97 ++++++++++ qui/motif_editor/MotifElement.hpp | 54 ++++++ qui/motif_editor/MotifModel.cpp | 223 +++++++++++++++++++++++ qui/motif_editor/MotifModel.hpp | 80 ++++++++ 12 files changed, 749 insertions(+), 197 deletions(-) delete mode 100644 qui/motif_editor/MotifDetail.cpp delete mode 100644 qui/motif_editor/MotifDetail.hpp create mode 100644 qui/motif_editor/MotifEditorDelegate.cpp create mode 100644 qui/motif_editor/MotifEditorDelegate.hpp create mode 100644 qui/motif_editor/MotifElement.cpp create mode 100644 qui/motif_editor/MotifElement.hpp create mode 100644 qui/motif_editor/MotifModel.cpp create mode 100644 qui/motif_editor/MotifModel.hpp diff --git a/qui/CMakeLists.txt b/qui/CMakeLists.txt index 0e8176c..c32176a 100644 --- a/qui/CMakeLists.txt +++ b/qui/CMakeLists.txt @@ -25,8 +25,9 @@ SET(MOC_HEADERS SubanalysisWindow.hpp ThresholdWidget.hpp ZoomWidget.hpp - motif_editor/MotifDetail.hpp + motif_editor/MotifModel.hpp motif_editor/MotifEditor.hpp + motif_editor/MotifEditorDelegate.hpp mussa_setup_dialog/IsFileValidator.hpp mussa_setup_dialog/MussaSetupDialog.hpp mussa_setup_dialog/MussaSetupWidget.hpp @@ -48,8 +49,10 @@ SET(GUI_SOURCES SubanalysisWindow.cpp ThresholdWidget.cpp ZoomWidget.cpp - motif_editor/MotifDetail.cpp motif_editor/MotifEditor.cpp + motif_editor/MotifEditorDelegate.cpp + motif_editor/MotifElement.cpp + motif_editor/MotifModel.cpp mussa_setup_dialog/IsFileValidator.cpp mussa_setup_dialog/MussaSetupDialog.cpp mussa_setup_dialog/MussaSetupWidget.cpp diff --git a/qui/MussaWindow.cpp b/qui/MussaWindow.cpp index a97f40e..6b0e8a7 100644 --- a/qui/MussaWindow.cpp +++ b/qui/MussaWindow.cpp @@ -360,7 +360,6 @@ void MussaWindow::saveAnalysisAs() if (fileNames.size() != 1) { return; } - fs::path save_path(fileNames[0].toStdString(), fs::native); // do you want to overwrite? if (fs::exists(save_path) and diff --git a/qui/motif_editor/MotifDetail.cpp b/qui/motif_editor/MotifDetail.cpp deleted file mode 100644 index 4774bdf..0000000 --- a/qui/motif_editor/MotifDetail.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include - -#include "qui/motif_editor/MotifDetail.hpp" - -#include -using namespace std; - -static const QRegExp iupacAlphabet("[ACTGUWRKYSMBHDVN]*", Qt::CaseInsensitive); -static const QRegExpValidator iupacValidator(iupacAlphabet, 0); - -MotifDetail::MotifDetail(QWidget *parent) - : QWidget(parent), - motif_color(1.0, 1.0, 1.0) -{ - setupWidget(); -} - -MotifDetail::MotifDetail(const MotifDetail &md) - : QWidget((QWidget *)md.parent()), - motif_color(md.motif_color), - motifText(md.motifText.displayText()), - motifName(md.motifName.displayText()) -{ -} - -MotifDetail::MotifDetail(std::string& m, Color& c, std::string& name, QWidget *parent) - : QWidget(parent), - motif_color(c), - motifText(m.c_str()), - motifName(name.c_str()) -{ - setupWidget(); -} - -void MotifDetail::setupWidget() -{ - QHBoxLayout *layout = new QHBoxLayout; - - enabledButton.setCheckState(Qt::Checked); - layout->addWidget(&enabledButton); - colorButton.setFlat(true); - colorButton.setPalette(QPalette(qcolor())); - colorButton.setAutoFillBackground(true); - connect(&colorButton, SIGNAL(clicked()), this, SLOT(promptColor())); - layout->addWidget(&colorButton); - motifText.setValidator(&iupacValidator); - layout->addWidget(&motifText); - layout->addWidget(&motifName); - - setLayout(layout); -} - -void MotifDetail::setMotif(const string &m) -{ - motifText.setText(m.c_str()); -} - -string MotifDetail::motif() const -{ - return motifText.text().toStdString(); -} - -void MotifDetail::setName(const std::string& name) -{ - motifName.setText(name.c_str()); -} - -std::string MotifDetail::name() const -{ - return motifName.text().toStdString(); -} - -void MotifDetail::setColor(const Color &c) -{ - motif_color = c; - colorButton.setPalette(QPalette(qcolor())); -} - -Color MotifDetail::color() const -{ - return motif_color; -} - -QColor MotifDetail::qcolor() const -{ - QColor qc; - qc.setRedF(motif_color.r()); - qc.setGreenF(motif_color.g()); - qc.setBlueF(motif_color.b()); - return qc; -} - -void MotifDetail::promptColor() -{ - QColor new_qcolor = QColorDialog::getColor(qcolor(), this); - Color new_color(new_qcolor.redF(), new_qcolor.greenF(), new_qcolor.blueF()); - setColor(new_color); -} - -bool MotifDetail::enabled() const -{ - return (enabledButton.checkState() == Qt::Checked); -} diff --git a/qui/motif_editor/MotifDetail.hpp b/qui/motif_editor/MotifDetail.hpp deleted file mode 100644 index f0336f4..0000000 --- a/qui/motif_editor/MotifDetail.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _MOTIF_DETAIL_H -#define _MOTIF_DETAIL_H - -#include - -#include -#include -#include -#include -#include -#include - -//#include "alg/sequence.hpp" -#include "alg/color.hpp" - -class MotifDetail : public QWidget -{ - Q_OBJECT - -public: - MotifDetail(QWidget *parent=0); - MotifDetail(const MotifDetail &); - MotifDetail(std::string& m, Color& c, std::string& name, QWidget *parent=0); - - void setMotif(const std::string& m); - std::string motif() const; - - void setName(const std::string& name); - std::string name() const; - - void setColor(const Color&); - Color color() const; - QColor qcolor() const; - - // is this motif enabled? - bool enabled() const; - -public slots: - void promptColor(); - -private: - void setupWidget(); - - Color motif_color; - - // widgets - QCheckBox enabledButton; - QPushButton colorButton; - QLineEdit motifText; - QLineEdit motifName; -}; -#endif diff --git a/qui/motif_editor/MotifEditor.cpp b/qui/motif_editor/MotifEditor.cpp index b27967d..8871358 100644 --- a/qui/motif_editor/MotifEditor.cpp +++ b/qui/motif_editor/MotifEditor.cpp @@ -9,33 +9,46 @@ using namespace std; MotifEditor::MotifEditor(Mussa *m, QWidget *parent) : QWidget(parent), analysis(m), - applyButton("set motifs") + editor_layout(new QVBoxLayout(parent)), + button_layout(new QHBoxLayout(parent)), + table(new QTableView(this)), + delegate(new MotifEditorDelegate(this)), + applyButton(new QPushButton("apply")), + model(0) { assert (m != 0); const set &motif = analysis->motifs(); vector motif_seq(motif.begin(), motif.end()); - connect(&applyButton, SIGNAL(clicked()), this, SLOT(updateMotifs())); - layout.addWidget(&applyButton); + applyButton->setFocusPolicy(Qt::StrongFocus); + connect(applyButton, SIGNAL(clicked()), this, SLOT(updateMotifs())); + button_layout->addStretch(); + button_layout->addWidget(applyButton); + + table->setItemDelegate(delegate); - for(size_t i=0; i != 10; ++i) - { - MotifDetail *detail = new MotifDetail; - if (i < motif_seq.size()) { - detail->setMotif(motif_seq[i].get_sequence()); - detail->setColor(analysis->colorMapper()->lookup("motif", motif_seq[i].get_sequence())); - } - motif_details.push_back(detail); - layout.addWidget(detail); - } - setLayout(&layout); + editor_layout->addWidget(table); + editor_layout->addLayout(button_layout); + setLayout(editor_layout); + + updateTitle(); + updateModel(); } -MotifEditor::MotifEditor(const MotifEditor& me) - : QWidget((QWidget*)me.parent()), - analysis(me.analysis), - applyButton(me.applyButton.text()) +void MotifEditor::updateModel() { + MotifModel *new_model = new MotifModel(); + + new_model->push_empty(); + + // if there was an old model, delete it + if (model) { + delete model; + } + // update the QTableView + model = new_model; + table->setModel(model); + updateView(); } void MotifEditor::updateMotifs() @@ -47,24 +60,37 @@ void MotifEditor::updateMotifs() analysis->colorMapper()->appendTypeColor("motif", motif_default); // add our motifs back - vector motifs; - vector colors; + vector new_motifs; + vector new_colors; - for(std::vector::iterator md_i = motif_details.begin(); - md_i != motif_details.end(); + for(MotifModel::iterator md_i = model->begin(); + md_i != model->end(); ++md_i) { - if ((*md_i)->motif().size() > 0 && (*md_i)->enabled()) { - Sequence new_motif((*md_i)->motif()); - new_motif.set_fasta_header((*md_i)->name()); - motifs.push_back((*md_i)->motif()); - colors.push_back((*md_i)->color()); + if (md_i->getSequence().size() > 0 && md_i->isEnabled()) { + new_motifs.push_back(md_i->getSequence()); + new_colors.push_back(md_i->getColor()); } } - analysis->set_motifs(motifs, colors); + analysis->set_motifs(new_motifs, new_colors); emit changedMotifs(); } -//MotifEditor:: +void MotifEditor::updateTitle() +{ + QString title("Motif Editor: "); + title.append(analysis->get_name().c_str()); + setWindowTitle(title); + +} + +void MotifEditor::updateView() +{ + for (int row = 0; row < model->rowCount(QModelIndex()); ++row) + table->resizeRowToContents(row); + for (int column = 0; column < model->columnCount(QModelIndex()); ++column) + table->resizeColumnToContents(column); +} + diff --git a/qui/motif_editor/MotifEditor.hpp b/qui/motif_editor/MotifEditor.hpp index b5a4579..b404dce 100644 --- a/qui/motif_editor/MotifEditor.hpp +++ b/qui/motif_editor/MotifEditor.hpp @@ -4,11 +4,13 @@ #include #include +#include +#include #include #include #include "alg/mussa.hpp" -#include "qui/motif_editor/MotifDetail.hpp" +#include "qui/motif_editor/MotifModel.hpp" class MotifEditor : public QWidget { @@ -16,22 +18,31 @@ class MotifEditor : public QWidget public: MotifEditor(Mussa* m, QWidget *parent=0); - MotifEditor(const MotifEditor&); + //! update our window title + void updateTitle(); + //! resize cells + void updateView(); + public slots: - // called to apply motif changes + //! create model and attach it to the table view + void updateModel(); + //! called to apply motif changes void updateMotifs(); signals: - // emitted when the use has applied the motif changes + //! emitted when the use has applied the motif changes void changedMotifs(); private: Mussa* analysis; - QPushButton applyButton; - QVBoxLayout layout; - - std::vector motif_details; + QPushButton *applyButton; + QVBoxLayout *editor_layout; + QHBoxLayout *button_layout; + QTableView *table; + MotifEditorDelegate *delegate; + + MotifModel *model; }; #endif diff --git a/qui/motif_editor/MotifEditorDelegate.cpp b/qui/motif_editor/MotifEditorDelegate.cpp new file mode 100644 index 0000000..61c5713 --- /dev/null +++ b/qui/motif_editor/MotifEditorDelegate.cpp @@ -0,0 +1,182 @@ +#include "qui/motif_editor/MotifEditorDelegate.hpp" + +#include +#include +#include +#include +#include +#include +#include + +MotifEditorDelegate::MotifEditorDelegate(QWidget *parent) : + QItemDelegate(parent) +{ +} + +QWidget *MotifEditorDelegate::createEditor( + QWidget *parent, + const QStyleOptionViewItem & /* option */, + const QModelIndex &index) const +{ + QWidget *widget = 0; + switch(index.column()) { + // case MotifModel::EnabledCell is handled in editorEvent + // case MotifModel::ColorCell: + // both of these should be text boxes + case MotifModel::NameCell: + case MotifModel::SequenceCell: + widget = new QLineEdit(parent); + widget->installEventFilter(const_cast(this)); + break; + default: + break; + } + return widget; +} + +//! check to see if our event is something that should "activate" our cell +static bool is_cell_activated(QEvent *event) +{ + // if a mouse releases on us we can toggle + if (event->type() == QEvent::MouseButtonRelease) { + return true; + // if we're a keypress + } else if (event->type() == QEvent::KeyPress) { + // try to convert to a KeyEvent + QKeyEvent *key_event = dynamic_cast(event); + // if we've gotten our key event, did the use hit the enter/return key? + if (key_event) { + if (key_event->key() == Qt::Key_Enter or key_event->key() == Qt::Key_Return) { + return true; + } + } + } + // all other cases don't toggle + return false; +} +bool MotifEditorDelegate::editorEvent( + QEvent *event, + QAbstractItemModel *model, + const QStyleOptionViewItem &option, + const QModelIndex & index ) +{ + switch (index.column()) { + case MotifModel::EnabledCell: + if (is_cell_activated(event)) { + QVariant value = index.model()->data(index, Qt::DisplayRole); + value = not value.toBool(); + model->setData(index, value); + return true; + } + break; + case MotifModel::ColorCell: + if (is_cell_activated(event)) { + QVariant value = index.model()->data(index, Qt::DisplayRole); + QColor old_color = value.value(); + QColor new_color = QColorDialog::getColor(old_color); + // color is not valid if user hit the cancel button on the color dialog + if (new_color.isValid()) { + model->setData(index, QVariant(new_color)); + } else { + model->setData(index, QVariant(old_color)); + } + return true; + } + break; + default: + break; + } + return this->QItemDelegate::editorEvent(event, model, option, index); +} + +void MotifEditorDelegate::setEditorData( + QWidget *editor, + const QModelIndex &index) const +{ + QVariant value = index.model()->data(index, Qt::DisplayRole); + switch(index.column()) { + // toggling MotifModel::EnabledCell is handled by editorEvent + // case MotifModel::ColorCell is handled by paint and editorEvent + + // both of these should be text boxes + case MotifModel::NameCell: + case MotifModel::SequenceCell: + static_cast(editor)->setText(value.toString()); + break; + default: + break; + } +} + +void MotifEditorDelegate::setModelData( + QWidget *editor, + QAbstractItemModel *model, + const QModelIndex &index) const +{ + QVariant value; + switch(index.column()) { + // both of these should be text boxes + case MotifModel::NameCell: + case MotifModel::SequenceCell: + value = static_cast(editor)->text(); + model->setData(index, value); + break; + default: + break; + } +} + +struct painter_save_restore { + painter_save_restore(QPainter *p) { painter = p; painter->save(); } + ~painter_save_restore() { painter->restore(); } + QPainter *painter; +}; + +static void drawHighlightIfNeeded(QPainter *painter, const QStyleOptionViewItem &option) +{ + if (option.state & QStyle::State_Selected) { + painter->setBrush(option.palette.highlight()); + painter->drawRect(option.rect); + } +} + +void MotifEditorDelegate::paint( + QPainter * painter, + const QStyleOptionViewItem & option, + const QModelIndex & index ) const +{ + painter_save_restore saved(painter); + + QVariant value = index.model()->data(index, Qt::DisplayRole); + switch(index.column()) { + case MotifModel::EnabledCell: + { + painter->setPen(Qt::NoPen); + drawHighlightIfNeeded(painter, option); + Qt::CheckState state = (value.toBool()) ? Qt::Checked : Qt::Unchecked; + this->QItemDelegate::drawCheck(painter, option, option.rect, state); + } + break; + case MotifModel::ColorCell: + { + painter->setPen(Qt::NoPen); + drawHighlightIfNeeded(painter, option); + QColor color(value.value()); + painter->setBrush(QBrush(color)); + painter->drawRoundRect(option.rect, 60, 60); + } + break; + default: + this->QItemDelegate::paint(painter, option, index); + break; + } +} + +void MotifEditorDelegate::updateEditorGeometry( + QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + editor->setGeometry(option.rect); +} + \ No newline at end of file diff --git a/qui/motif_editor/MotifEditorDelegate.hpp b/qui/motif_editor/MotifEditorDelegate.hpp new file mode 100644 index 0000000..040fb9a --- /dev/null +++ b/qui/motif_editor/MotifEditorDelegate.hpp @@ -0,0 +1,34 @@ +#ifndef MOTIFEDITORDELEGATE_HPP_ +#define MOTIFEDITORDELEGATE_HPP_ + +#include +#include + +#include "qui/motif_editor/MotifModel.hpp" + +class MotifEditorDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + MotifEditorDelegate(QWidget *parent=0); + + //! construct a editor widget for a given index. + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem & option, const QModelIndex & index ); + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; +private: +}; + +#endif /*MOTIFEDITORDELEGATE_HPP_*/ diff --git a/qui/motif_editor/MotifElement.cpp b/qui/motif_editor/MotifElement.cpp new file mode 100644 index 0000000..5bbda0e --- /dev/null +++ b/qui/motif_editor/MotifElement.cpp @@ -0,0 +1,97 @@ +#include "qui/motif_editor/MotifEditor.hpp" + +#include + +MotifElement::MotifElement() : + enabled(true), + color(Color(1.0,0.0,0.0,1.0)), + motif() +{ +} + +MotifElement::MotifElement(const Sequence& seq, Color c) : + enabled(true), + color(c), + motif(seq) +{ +} + +bool MotifElement::isEmpty() const +{ + if (motif.get_name().size() == 0 and motif.size() == 0) { + return true; + } + return false; +} +//! should we search the analysis for this element? +bool MotifElement::isEnabled() const +{ + return enabled; +} + +//! set the state for searching the analysis for this element? +void MotifElement::setEnabled(bool enabled_) +{ + enabled = enabled_; +} + +//! return color should this motif be drawn as +Color MotifElement::getColor() const +{ + return color; +} + +//! set what color this motif should be drawn as +void MotifElement::setColor(const Color& c) +{ + color = c; +} + +//! return color should this motif be drawn as +QColor MotifElement::getQColor() const +{ + QColor qcolor; + qcolor.setRgbF(color.r(), color.g(), color.b(), color.a()); + return qcolor; +} + +//! set what color this motif should be drawn as +void MotifElement::setQColor(const QColor& c) +{ + color = Color(c.redF(), c.greenF(), c.blueF(), c.alphaF()); +} + +//! return sequence +const Sequence& MotifElement::getSequence() const +{ + return motif; +} + +//! set sequence +void MotifElement::setSequence(const Sequence& seq) +{ + motif = seq; +} + +//! set sequence text +void MotifElement::setSequence(const std::string& seq_text) +{ + motif.set_sequence(seq_text); +} + +QString MotifElement::getSequenceText() const +{ + return QString(motif.get_sequence().c_str()); +} + +//! get sequence name +std::string MotifElement::getName() const +{ + return motif.get_name(); +} + +//! set sequence name +void MotifElement::setName(const std::string& seq_name) +{ + motif.set_fasta_header(seq_name); +} \ No newline at end of file diff --git a/qui/motif_editor/MotifElement.hpp b/qui/motif_editor/MotifElement.hpp new file mode 100644 index 0000000..76687d8 --- /dev/null +++ b/qui/motif_editor/MotifElement.hpp @@ -0,0 +1,54 @@ +#ifndef MOTIFELEMENT_HPP_ +#define MOTIFELEMENT_HPP_ + +#include +#include + +#include "alg/color.hpp" +#include "alg/sequence.hpp" + +class MotifElement { +public: + MotifElement(); + MotifElement(const Sequence& seq, Color c=Color(1.0,0.0,0.0,1.0)); + + //! is the element "empty" + bool isEmpty() const; + + //! should we search the analysis for this element? + bool isEnabled() const; + //! set the state for searching the analysis for this element? + void setEnabled(bool); + + //! return color should this motif be drawn as + Color getColor() const; + //! set what color this motif should be drawn as + void setColor(const Color& c); + //! return QColor should this motif should be drawn as + QColor getQColor() const; + //! set color using QColor + void setQColor(const QColor& c); + + //! return sequence + const Sequence& getSequence() const; + //! set sequence + void setSequence(const Sequence& seq); + //! set sequence text + void setSequence(const std::string& seq_text); + //! get sequence text + QString getSequenceText() const; + + //! get sequence name + std::string getName() const; + //! set sequence name + void setName(const std::string& seq_name); + +protected: + //! search for motif + bool enabled; + //! store motif color + Color color; + //! store sequence + Sequence motif; +}; +#endif /*MOTIFELEMENT_HPP_*/ diff --git a/qui/motif_editor/MotifModel.cpp b/qui/motif_editor/MotifModel.cpp new file mode 100644 index 0000000..1bb05e4 --- /dev/null +++ b/qui/motif_editor/MotifModel.cpp @@ -0,0 +1,223 @@ +#include "qui/motif_editor/MotifModel.hpp" + +#include + +MotifModel::MotifModel(QObject *parent) + : QAbstractTableModel(parent) +{ +} + +void MotifModel::assign( + MotifModel::size_type num, + const MotifElement& val +) +{ + motifs.assign(num, val); +} + +MotifElement& MotifModel::at( + MotifModel::size_type index +) +{ + return motifs.at(index); +} + +MotifElement& MotifModel::back() +{ + return motifs.back(); +} + +MotifModel::iterator MotifModel::begin() +{ + return motifs.begin(); +} + +MotifModel::const_iterator MotifModel::begin() const +{ + return motifs.begin(); +} + +void MotifModel::clear() +{ + if (motifs.size() != 0) { + beginRemoveRows(QModelIndex(), 0, motifs.size()-1); + motifs.clear(); + endRemoveRows(); + } +} + +MotifModel::iterator MotifModel::end() +{ + return motifs.end(); +} + +MotifModel::const_iterator MotifModel::end() const +{ + return motifs.end(); +} + +bool MotifModel::empty() const +{ + return motifs.empty(); +} + +MotifElement& MotifModel::operator[]( + MotifModel::size_type index +) +{ + return motifs[index]; +} + +void MotifModel::pop_back() +{ + int last_element = motifs.size()-1; + if (last_element >= 0) { + beginRemoveRows(QModelIndex(), last_element, last_element); + motifs.pop_back(); + endRemoveRows(); + } +} + +void MotifModel::push_back(const MotifElement& item) +{ + int last_element = motifs.size(); + beginInsertRows(QModelIndex(), last_element, last_element); + motifs.push_back(item); + endInsertRows(); +} + +void MotifModel::push_empty() +{ + MotifElement blank; + push_back(blank); +} + +MotifModel::size_type MotifModel::size() const +{ + return motifs.size(); +} + +int +MotifModel::rowCount( const QModelIndex& parent) const +{ + return motifs.size(); +} + +int +MotifModel::columnCount(const QModelIndex& parent) const +{ + return 4; +} + +QVariant +MotifModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= motifs.size()) + return QVariant(); + + if (index.column() >= 4) + return QVariant(); + + const MotifElement& motif = motifs[index.row()]; + if (role == Qt::DisplayRole) { + switch (index.column() ) { + case MotifModel::EnabledCell: + return QVariant(motif.isEnabled()); + break; + case MotifModel::ColorCell: + return QVariant(motif.getQColor()); + break; + case MotifModel::NameCell: + return QString(motif.getName().c_str()); + break; + case MotifModel::SequenceCell: + return QString(motif.getSequenceText()); + break; + } + } + return QVariant(); +} + +QVariant +MotifModel::headerData( + int section, + Qt::Orientation orientation, + int role +) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch(section) { + case MotifModel::EnabledCell: + return QString("Enabled"); + break; + case MotifModel::ColorCell: + return QString("Color"); + break; + case MotifModel::NameCell: + return QString("Name"); + break; + case MotifModel::SequenceCell: + return QString("Sequence"); + break; + default: + return QVariant(); + break; + } + } + return QVariant(); +} + +bool MotifModel::setData( + const QModelIndex& index, + const QVariant &value, + int role +) +{ + if (index.isValid() and role == Qt::EditRole) { + MotifElement& motif = motifs[index.row()]; + switch(index.column()) { + case MotifModel::EnabledCell: + motif.setEnabled(value.toBool()); + break; + case MotifModel::ColorCell: + motif.setQColor(value.value()); + break; + case MotifModel::NameCell: + motif.setName(value.toString().toStdString()); + break; + case MotifModel::SequenceCell: + motif.setSequence(value.toString().toStdString()); + break; + default: + return false; + break; + } + emit dataChanged(index, index); + // automatically grow the list of motifs when the last one is full + if (not back().isEmpty()) { + push_empty(); + } + return true; + } else { + return false; + } +} + +Qt::ItemFlags MotifModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + // first column is not editable + if (index.column() == 0) + return QAbstractItemModel::flags(index); + else + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + diff --git a/qui/motif_editor/MotifModel.hpp b/qui/motif_editor/MotifModel.hpp new file mode 100644 index 0000000..02ffe7c --- /dev/null +++ b/qui/motif_editor/MotifModel.hpp @@ -0,0 +1,80 @@ +#ifndef _MOTIF_MODEL_HPP_ +#define _MOTIF_MODEL_HPP_ + +#include "alg/sequence.hpp" +#include "qui/motif_editor/MotifElement.hpp" +#include "qui/motif_editor/MotifEditorDelegate.hpp" + +#include + +#include + +class MotifModel : public QAbstractTableModel +{ + Q_OBJECT + + public: + MotifModel(QObject *parent = 0); + + typedef std::vector model_type; + typedef model_type::size_type size_type; + typedef model_type::iterator iterator; + typedef model_type::const_iterator const_iterator; + + enum cell_names { EnabledCell, ColorCell, NameCell, SequenceCell }; + //! \defgroup VectorInterface + //! \addtogroup VectorInterface + //! \@{ + //! assign num copies of val to our vector + void assign(size_type num, const MotifElement& val); + //! return a specific element + MotifElement& at(size_type index); + //! return the last element + MotifElement& back(); + //! return an iterator to the beginning of the model + iterator begin(); + //! return an iterator to the beginning of the model + const_iterator begin() const; + //! empty the model + void clear(); + //! return an iterator to the end of the model + iterator end(); + //! return an iterator to the end of the model + const_iterator end() const; + //! is the model empty? + bool empty() const; + //! return a specific element + MotifElement& operator[](size_type index); + //! remove the last element from our model + void pop_back(); + //! add a MotifElement location to the end of our model + void push_back(const MotifElement&); + //! add an empty element to the end of our model + void push_empty(); + //! how many elements are in our model. + size_type size() const; + //! \@} + + //! \defgroup QtModelInterface + //! \addtogroup QtModel + //! \@{ + int rowCount(const QModelIndex& parent=QModelIndex()) const; + int columnCount(const QModelIndex& parent=QModelIndex()) const; + QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; + + QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; + + bool setData(const QModelIndex& index, const QVariant &value, + int role=Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex& index) const; + + //bool insertRows(int row, int count, + // const QModelIndex& parent=QModelIndex()); + //bool removeRows(int row, int count, + // const QModelIndex& parent=QModelIndex()); + //! \@} + private: + model_type motifs; +}; + +#endif -- 2.30.2