better motif editor
authorDiane Trout <diane@caltech.edu>
Fri, 29 Sep 2006 01:01:06 +0000 (01:01 +0000)
committerDiane Trout <diane@caltech.edu>
Fri, 29 Sep 2006 01:01:06 +0000 (01:01 +0000)
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.

12 files changed:
qui/CMakeLists.txt
qui/MussaWindow.cpp
qui/motif_editor/MotifDetail.cpp [deleted file]
qui/motif_editor/MotifDetail.hpp [deleted file]
qui/motif_editor/MotifEditor.cpp
qui/motif_editor/MotifEditor.hpp
qui/motif_editor/MotifEditorDelegate.cpp [new file with mode: 0644]
qui/motif_editor/MotifEditorDelegate.hpp [new file with mode: 0644]
qui/motif_editor/MotifElement.cpp [new file with mode: 0644]
qui/motif_editor/MotifElement.hpp [new file with mode: 0644]
qui/motif_editor/MotifModel.cpp [new file with mode: 0644]
qui/motif_editor/MotifModel.hpp [new file with mode: 0644]

index 0e8176cef3af4c945b74a876d5c55be1673513db..c32176a607c0ad004b1ba792b46a909dea182fb0 100644 (file)
@@ -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
index a97f40ec804da19725efcdad1bd0acd215763bc0..6b0e8a7e9e07614ea9e596e12bde4a2dfd4660eb 100644 (file)
@@ -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 (file)
index 4774bdf..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <QColorDialog>
-#include <QHBoxLayout>
-#include <QRegExp>
-
-#include "qui/motif_editor/MotifDetail.hpp"
-
-#include <iostream>
-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 (file)
index f0336f4..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef _MOTIF_DETAIL_H
-#define _MOTIF_DETAIL_H
-
-#include <string>
-
-#include <QCheckBox>
-#include <QColor>
-#include <QLineEdit>
-#include <QPushButton>
-#include <QRegExpValidator>
-#include <QWidget>
-
-//#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
index b27967dd2037f8c98b03018a15756cb3cbd6d186..88713588e295d45c5b0cbb2229b6b92cb00c0d1a 100644 (file)
@@ -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<Sequence> &motif = analysis->motifs();
   vector<Sequence> 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<Sequence> motifs;
-  vector<Color> colors;
+  vector<Sequence> new_motifs;
+  vector<Color> new_colors;
 
-  for(std::vector<MotifDetail *>::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);
+}
+
 
index b5a457957c653dc33a5c94e0780e63c442f7be49..b404dce967a8567de1c5f03b4d9d14036c3395bf 100644 (file)
@@ -4,11 +4,13 @@
 #include <vector>
 
 #include <QPushButton>
+#include <QScrollArea>
+#include <QTableView>
 #include <QVBoxLayout>
 #include <QWidget>
 
 #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<MotifDetail *> 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 (file)
index 0000000..61c5713
--- /dev/null
@@ -0,0 +1,182 @@
+#include "qui/motif_editor/MotifEditorDelegate.hpp"
+
+#include <Qt>
+#include <QCheckBox>
+#include <QColorDialog>
+#include <QEvent>
+#include <QKeyEvent>
+#include <QLineEdit>
+#include <QPainter>
+
+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<MotifEditorDelegate*>(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<QKeyEvent *>(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>();
+        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<QLineEdit*>(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<QLineEdit*>(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<QColor>());
+      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 (file)
index 0000000..040fb9a
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MOTIFEDITORDELEGATE_HPP_
+#define MOTIFEDITORDELEGATE_HPP_
+
+#include <QItemDelegate>
+#include <QWidget>
+
+#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 (file)
index 0000000..5bbda0e
--- /dev/null
@@ -0,0 +1,97 @@
+#include "qui/motif_editor/MotifEditor.hpp"
+
+#include <iostream>
+
+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 (file)
index 0000000..76687d8
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MOTIFELEMENT_HPP_
+#define MOTIFELEMENT_HPP_
+
+#include <QColor>
+#include <QString>
+
+#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 (file)
index 0000000..1bb05e4
--- /dev/null
@@ -0,0 +1,223 @@
+#include "qui/motif_editor/MotifModel.hpp"
+
+#include <QColor>
+
+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<QColor>());
+        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 (file)
index 0000000..02ffe7c
--- /dev/null
@@ -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 <QAbstractTableModel>
+
+#include <vector>
+
+class MotifModel : public QAbstractTableModel
+{
+  Q_OBJECT
+
+  public:
+    MotifModel(QObject *parent = 0);
+
+    typedef std::vector<MotifElement> 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