diff --git a/modules/gui/src/CMakeLists.txt b/modules/gui/src/CMakeLists.txt index da9df49e3eb9f6886b5a0eea4bd10e5fa813c213..ea38cf4c0ad9fe88403b10777f51bfd2d16813bf 100644 --- a/modules/gui/src/CMakeLists.txt +++ b/modules/gui/src/CMakeLists.txt @@ -31,6 +31,7 @@ seq_text_painter.hh sequence_delegate.hh sequence_model.hh sequence_row.hh +sequence_search_bar.hh sequence_table_view.hh sequence_viewer.hh tick_painter.hh @@ -220,6 +221,7 @@ sequence/seq_text_painter.cc sequence/sequence_delegate.cc sequence/sequence_model.cc sequence/sequence_row.cc +sequence/sequence_search_bar.cc sequence/sequence_table_view.cc sequence/sequence_viewer.cc sequence/tick_painter.cc @@ -351,6 +353,7 @@ sequence/seq_text_painter.hh sequence/sequence_delegate.hh sequence/sequence_model.hh sequence/sequence_row.hh +sequence/sequence_search_bar.hh sequence/sequence_table_view.hh sequence/sequence_viewer.hh sequence/tick_painter.hh diff --git a/modules/gui/src/sequence/sequence_model.cc b/modules/gui/src/sequence/sequence_model.cc index ddac0e2a8db67446cb5bb75f32d699cf7a8c2c96..a44cf3a95c68ee1e6d9aec181ff539c14785810c 100644 --- a/modules/gui/src/sequence/sequence_model.cc +++ b/modules/gui/src/sequence/sequence_model.cc @@ -179,6 +179,25 @@ QModelIndexList SequenceModel::GetModelIndexes(gfx::EntityP& entity, const mol:: return list; } +QModelIndexList SequenceModel::GetModelIndexes(const QString& subject, const QString& sequence_name) +{ + QModelIndexList list; + for (int i = 0; i<objects_.size(); i++){ + ViewObject* object = objects_[i]; + QMap<int, QList<int> > indexes = object->GetIndexesForSubject(subject,sequence_name); + QMapIterator< int, QList<int> > i(indexes); + while (i.hasNext()) { + i.next(); + int row = this->GetGlobalRow(object, i.key()); + const QList<int>& index_list = i.value(); + for(int i=0; i<index_list.size(); i++){ + list.append(this->index(row,index_list[i])); + } + } + } + return list; +} + void SequenceModel::SelectionChanged(const QItemSelection& sel, const QItemSelection& desel) { QMap<int,QPair<QSet<int>,QSet<int> > > sel_map; diff --git a/modules/gui/src/sequence/sequence_model.hh b/modules/gui/src/sequence/sequence_model.hh index 1c8b27f6fca5cf94f9eefff0bd938025870b2180..ea42f348fbb0df21d0d590c1f990f7c4bff7545c 100644 --- a/modules/gui/src/sequence/sequence_model.hh +++ b/modules/gui/src/sequence/sequence_model.hh @@ -50,6 +50,8 @@ public: void RemoveGfxEntity(gfx::EntityP& entity); QModelIndexList GetModelIndexes(gfx::EntityP& entity, const mol::EntityView& view); + QModelIndexList GetModelIndexes(const QString& subject, const QString& sequence_name=QString()); + int GetGlobalRow(ViewObject* obj, int row) const; diff --git a/modules/gui/src/sequence/sequence_search_bar.cc b/modules/gui/src/sequence/sequence_search_bar.cc new file mode 100644 index 0000000000000000000000000000000000000000..9ae88675e2468dbe5083cbb642df653fd563c67a --- /dev/null +++ b/modules/gui/src/sequence/sequence_search_bar.cc @@ -0,0 +1,125 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2010 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ +/* + Author: Marco Biasini + */ + +#include "sequence_search_bar.hh" + +#include <QHBoxLayout> +#include <QLineEdit> +#include <QPainter> +#include <QPaintEvent> +#include <QLabel> +#include <QKeyEvent> + +namespace ost { namespace gui { + +SeqSearchBar::SeqSearchBar(QWidget* parent): + QWidget(parent) +{ + subject_=new QLineEdit(this); + search_all_=new QCheckBox("search in all sequences", this); + search_in_=new QComboBox(this); + QHBoxLayout* l= new QHBoxLayout(this); + l->addSpacing(2); + l->addWidget(subject_, 0); + l->setStretch(0,1); + l->addWidget(search_all_, 0); + QLabel* label=new QLabel("search in:", this); + l->addSpacing(10); + l->addWidget(label, 0); + l->addWidget(search_in_, 0); + //subject_->setMaximumWidth(200); + setLayout(l); + l->setSizeConstraint(QLayout::SetMaximumSize); + l->setMargin(1); + search_all_->setCheckState(Qt::Checked); + search_in_->setEnabled(false); +#if defined(__APPLE__) + subject_->setAttribute(Qt::WA_MacSmallSize, true); + search_all_->setAttribute(Qt::WA_MacSmallSize, true); + search_in_->setAttribute(Qt::WA_MacSmallSize, true); + label->setAttribute(Qt::WA_MacSmallSize, true); +#endif + connect(subject_, SIGNAL(textChanged(const QString&)), this, + SLOT(OnSubjectChanged(const QString&))); + connect(search_all_, SIGNAL(stateChanged(int)), this, + SLOT(OnSearchAllChanged(int))); + connect(search_in_, SIGNAL(currentIndexChanged(int)), this, + SLOT(OnSearchInChanged(int))); +} + +void SeqSearchBar::UpdateItems(const QStringList& sequences) +{ + search_in_->clear(); + + for(int i=0;i< sequences.size(); i++){ + search_in_->addItem(sequences[i]); + } + + if (sequences.empty()) { + search_all_->setCheckState(Qt::Checked); + search_in_->setEnabled(false); + } + subject_->setFocus(Qt::ActiveWindowFocusReason); + subject_->selectAll(); +} +void SeqSearchBar::OnSearchAllChanged(int state) +{ + if (state==Qt::Unchecked) { + search_in_->setEnabled(true); + } else { + search_in_->setEnabled(false); + } + emit Changed(subject_->text(), search_all_->checkState()==Qt::Checked, + search_in_->currentText()); +} + +void SeqSearchBar::OnSearchInChanged(int index) +{ + emit Changed(subject_->text(), search_all_->checkState()==Qt::Checked, + search_in_->currentText()); +} + +void SeqSearchBar::OnSubjectChanged(const QString& str) +{ + emit Changed(str, search_all_->checkState()==Qt::Checked, + search_in_->currentText()); +} + +void SeqSearchBar::paintEvent(QPaintEvent* paint_event) +{ + QPainter p(this); + p.setBrush(QBrush(QColor(Qt::blue).lighter(300))); + p.setPen(QPen(QColor(Qt::blue).lighter(200))); + p.drawRect(rect()); + paint_event->accept(); +} + +void SeqSearchBar::keyPressEvent(QKeyEvent* key_event) +{ + if (key_event->key()==Qt::Key_Escape) { + this->hide(); + key_event->accept(); + } + QWidget::keyPressEvent(key_event); +} + +}} diff --git a/modules/gui/src/sequence/sequence_search_bar.hh b/modules/gui/src/sequence/sequence_search_bar.hh new file mode 100644 index 0000000000000000000000000000000000000000..865a42be22c30a828ba24285319f8f6d5bb592cc --- /dev/null +++ b/modules/gui/src/sequence/sequence_search_bar.hh @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------------ +// This file is part of the OpenStructure project <www.openstructure.org> +// +// Copyright (C) 2008-2010 by the OpenStructure authors +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation; either version 3.0 of the License, or (at your option) +// any later version. +// This library is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +//------------------------------------------------------------------------------ +#ifndef OST_SEQUENCE_VIEWER_SEQUENCE_SEARCH_BAR_HH +#define OST_SEQUENCE_VIEWER_SEQUENCE_SEARCH_BAR_HH + +/* + Author: Marco Biasini + */ +#include <ost/gui/module_config.hh> +#include <ost/seq/alignment_handle.hh> +#include <QWidget> +#include <QComboBox> +#include <QCheckBox> +#include <QSet> + +namespace ost { namespace gui { + +/// \brief search bar to search in multiple sequence alignment +class DLLEXPORT_OST_GUI SeqSearchBar : public QWidget { + Q_OBJECT +public: + SeqSearchBar(QWidget* parent=NULL); + + void UpdateItems(const QStringList& sequences); +signals: + void Changed(const QString&, bool, const QString&); +public slots: + void OnSubjectChanged(const QString&); + void OnSearchInChanged(int); + void OnSearchAllChanged(int); +protected: + virtual void paintEvent(QPaintEvent* paint_event); + virtual void keyPressEvent(QKeyEvent* key_event); +private: + seq::AlignmentHandle ali_; + QLineEdit* subject_; + QCheckBox* search_all_; + QComboBox* search_in_; +}; + +}} + +#endif diff --git a/modules/gui/src/sequence/sequence_table_view.cc b/modules/gui/src/sequence/sequence_table_view.cc index 877c423eaaf8c23564f4303c85f18cab3e481444..4529101fed75e0805f086a646287e8e4a1ebecb2 100644 --- a/modules/gui/src/sequence/sequence_table_view.cc +++ b/modules/gui/src/sequence/sequence_table_view.cc @@ -95,8 +95,7 @@ void SequenceTableView::InitStaticColumn() static_column_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); static_column_->show(); static_column_->setStyleSheet("QTableView { border: 0px;" - "background-color: #FFFFFF;" - "selection-background-color: #EEEEEE}" + "background-color: #FFFFFF}" "QTableView::item{ border: none;" "padding: 0px; border-width: 0px; margin: 0px;}"); static_column_->setShowGrid(false); @@ -130,8 +129,7 @@ void SequenceTableView::InitStaticRow() static_row_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); static_row_->show(); static_row_->setStyleSheet("QTableView { border: 0px;" - "background-color: #FFFFFF;" - "selection-background-color: #EEEEEE}" + "background-color: #FFFFFF}" "QTableView::item{ border: none;" "padding: 0px; border-width: 0px; margin: 0px;}"); static_row_->setShowGrid(false); @@ -169,8 +167,7 @@ void SequenceTableView::InitStaticField(){ static_field_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); static_field_->show(); static_field_->setStyleSheet("QTableView { border: 0px;" - "background-color: #FFFFFF;" - "selection-background-color: #EEEEEE}" + "background-color: #FFFFFF}" "QTableView::item{ border: none;" "padding: 0px; border-width: 0px; margin: 0px;}"); static_field_->setShowGrid(false); diff --git a/modules/gui/src/sequence/sequence_viewer.cc b/modules/gui/src/sequence/sequence_viewer.cc index fd6a3cf2bcecfcb040758d1f80bb1b624f71b4c6..722c962d7bfb8fee5d1955b51d12bf300f7d59fe 100644 --- a/modules/gui/src/sequence/sequence_viewer.cc +++ b/modules/gui/src/sequence/sequence_viewer.cc @@ -22,10 +22,11 @@ */ #include <boost/pointer_cast.hpp> -#include <QVBoxLayout> -#include <QPushButton> -#include <QHeaderView> #include <QAbstractItemView> +#include <QHeaderView> +#include <QPushButton> +#include <QShortcut> +#include <QVBoxLayout> #include <ost/mol/chain_view.hh> #include <ost/mol/entity_view.hh> @@ -75,6 +76,13 @@ SequenceViewerV2::SequenceViewerV2(QWidget* parent): Widget(NULL,parent) layout->setMargin(0); layout->setSpacing(0); + seq_search_bar_ = new SeqSearchBar(this); + seq_search_bar_->hide(); + layout->addWidget(seq_search_bar_); + QShortcut* shortcut=new QShortcut(QKeySequence(tr("Ctrl+F")), this); + connect(shortcut, SIGNAL(activated()), this, SLOT(FindInSequence())); + connect(seq_search_bar_, SIGNAL(Changed(const QString&, bool, const QString&)), this, SLOT(OnSearchBarUpdate(const QString&, bool, const QString&))); + seq_table_view_ = new SequenceTableView(model_); layout->addWidget(seq_table_view_); this->setLayout(layout); @@ -106,6 +114,7 @@ void SequenceViewerV2::NodeAdded(const gfx::GfxNodeP& n) seq_table_view_->resizeColumnsToContents(); seq_table_view_->resizeRowsToContents(); } + this->UpdateSearchBar(); } void SequenceViewerV2::NodeRemoved(const gfx::GfxNodeP& node) @@ -115,6 +124,16 @@ void SequenceViewerV2::NodeRemoved(const gfx::GfxNodeP& node) } } +void SequenceViewerV2::UpdateSearchBar() +{ + QStringList sequence_names_; + for(int i = 1; i< model_->rowCount(); i++){ + QString name = model_->data(model_->index(i,0),Qt::DisplayRole).toString(); + sequence_names_.append(name); + } + seq_search_bar_->UpdateItems(sequence_names_); +} + void SequenceViewerV2::SelectionModelChanged(const QItemSelection& sel, const QItemSelection& desel) { gfx::Scene::Instance().DetachObserver(this); @@ -126,21 +145,10 @@ void SequenceViewerV2::SelectionChanged(const gfx::GfxObjP& o, const mol::EntityView& view) { disconnect(seq_table_view_->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(SelectionModelChanged(const QItemSelection&, const QItemSelection&))); - QItemSelectionModel* model = seq_table_view_->selectionModel(); gfx::EntityP entity=boost::dynamic_pointer_cast<gfx::Entity>(o); if(entity){ const QModelIndexList& list = model_->GetModelIndexes(entity, view); - QSet<int> rows_visited; - for(int i = 0; i<list.size(); i++){ - int row =list[i].row(); - if(!rows_visited.contains(row)){ - model->select(list[i],QItemSelectionModel::Rows|QItemSelectionModel::Deselect); - rows_visited.insert(row); - } - } - for(int i = 0; i<list.size(); i++){ - model->select(list[i],QItemSelectionModel::Select); - } + this->SelectList(list); } connect(seq_table_view_->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(SelectionModelChanged(const QItemSelection&, const QItemSelection&))); } @@ -172,6 +180,46 @@ void SequenceViewerV2::MouseWheelEvent(QWheelEvent* event) event->accept(); } +void SequenceViewerV2::FindInSequence() +{ + if(seq_search_bar_->isHidden()){ + seq_search_bar_->show(); + } + else{ + seq_search_bar_->hide(); + } +} + +void SequenceViewerV2::OnSearchBarUpdate(const QString& subject, + bool search_in_all, const QString& name) +{ + seq_table_view_->selectionModel()->clear(); + if(search_in_all){ + const QModelIndexList& list = model_->GetModelIndexes(subject); + this->SelectList(list); + } + else{ + const QModelIndexList& list = model_->GetModelIndexes(subject,name); + this->SelectList(list); + } +} + +void SequenceViewerV2::SelectList(const QModelIndexList& list) +{ + QItemSelectionModel* model = seq_table_view_->selectionModel(); + QSet<int> rows_visited; + for(int i = 0; i<list.size(); i++){ + int row =list[i].row(); + if(!rows_visited.contains(row)){ + model->select(list[i],QItemSelectionModel::Rows|QItemSelectionModel::Deselect); + rows_visited.insert(row); + } + } + for(int i = 0; i<list.size(); i++){ + model->select(list[i],QItemSelectionModel::Select); + } +} + SequenceViewerV2::~SequenceViewerV2(){ gfx::Scene::Instance().DetachObserver(this); } diff --git a/modules/gui/src/sequence/sequence_viewer.hh b/modules/gui/src/sequence/sequence_viewer.hh index 3940c68fad19cfed610c9911242cb74cfccbcd98..112bb1c4b5ab92b591873c3b66140324d56d00ba 100644 --- a/modules/gui/src/sequence/sequence_viewer.hh +++ b/modules/gui/src/sequence/sequence_viewer.hh @@ -32,6 +32,7 @@ #include <ost/gui/module_config.hh> +#include "sequence_search_bar.hh" #include "sequence_model.hh" #include "sequence_table_view.hh" @@ -51,7 +52,18 @@ public: virtual bool Restore(const QString&){return true;}; virtual bool Save(const QString&){return true;}; +public slots: +/// \internal +void OnSearchBarUpdate(const QString&, bool, const QString&); + +private slots: +/// \brief show sequence search bar +void FindInSequence(); + private: + void UpdateSearchBar(); + void SelectList(const QModelIndexList& list); + SeqSearchBar* seq_search_bar_; SequenceModel* model_; SequenceTableView* seq_table_view_; diff --git a/modules/gui/src/sequence/view_object.cc b/modules/gui/src/sequence/view_object.cc index 01d80818a08b725cdd655dec051f1b9f5b204aff..88dfede657186f09ebef54ba712c8d1c10ca5396 100644 --- a/modules/gui/src/sequence/view_object.cc +++ b/modules/gui/src/sequence/view_object.cc @@ -229,6 +229,35 @@ QMap<int, QList<int> > ViewObject::GetIndexesForView(const mol::EntityView& view } } +QMap<int, QList<int> > ViewObject::GetIndexesForSubject(const QString& subject, const QString& sequence_name) +{ + if(subject.size()==0){ + return QMap<int, QList<int> >(); + } + QMap<int, QList<int> > selected_indexes; + String subject_str = subject.toStdString(); + for(int i=0; i< rows_.size(); i++){ + if(SequenceRow* secstr_row = qobject_cast<SequenceRow*>(rows_[i])){ + if(sequence_name.size()==0 || sequence_name==secstr_row->GetName()){ + seq::SequenceHandle seq = secstr_row->GetSequence(); + String seq_str=seq.GetString(); + size_t pos=0; + size_t first=String::npos; + while ((pos=seq_str.find(subject_str, pos))!=String::npos) { + if (first==String::npos) { + first=pos; + } + for(int j=0; j < subject.size(); j++){ + selected_indexes[i].append(pos+j+1); + } + pos+=subject.length(); + } + } + } + } + return selected_indexes; +} + Qt::ItemFlags ViewObject::Flags(int row, int column) const { if(row<0 || row >= rows_.size())return Qt::NoItemFlags; diff --git a/modules/gui/src/sequence/view_object.hh b/modules/gui/src/sequence/view_object.hh index a6d99bc74c2a84d3c91f2d273c9855136e1ce502..4e88d9eb6e919b7d222b5ecc075522022417f64c 100644 --- a/modules/gui/src/sequence/view_object.hh +++ b/modules/gui/src/sequence/view_object.hh @@ -78,6 +78,7 @@ public: void ZoomOut(); QMap<int, QList<int> > GetIndexesForView(const mol::EntityView& view); + QMap<int, QList<int> > GetIndexesForSubject(const QString& subject, const QString& sequence_name=QString()); private: QList<BaseRow*> rows_;