partial implementation of sequence track copy
authorDiane Trout <diane@caltech.edu>
Sat, 10 Jun 2006 02:38:47 +0000 (02:38 +0000)
committerDiane Trout <diane@caltech.edu>
Sat, 10 Jun 2006 02:38:47 +0000 (02:38 +0000)
this includes the code to track the portion of a sequence track that
has been selected, and provides a MussaWindow option to "copy" the selection
as a fasta file.

However the selection doesn't go to the clipboard yet, and the
MussaAlignedWindow also needs the option.

The actual Qt Slot that calls the glseqbrowser::copySelectedTrackAsFasta
is in the SequenceBrowserWidget.

alg/glseqbrowser.cpp
alg/glseqbrowser.hpp
alg/sequence.cpp
alg/sequence.hpp
alg/test/test_sequence.cpp
qui/MussaWindow.cpp
qui/MussaWindow.hpp
qui/seqbrowser/SequenceBrowserWidget.cpp
qui/seqbrowser/SequenceBrowserWidget.hpp

index f8a7b8c694834905c28ca9e4f634d4429f4581d1..560662b0188e1fb3b1a790e538626e1debd64da4 100644 (file)
@@ -62,7 +62,7 @@ void GlSeqBrowser::paintGL() const
   glFlush();
 }
 
-void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
+void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize, const rect<float>& r)
 {
   GLuint *ptr;
   GLuint names;
@@ -74,6 +74,7 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize
   GLuint path_index = 0;
   GLuint pair_key_0 = 0;
   GLuint pair_key_1 = 0;
+  TrackRegion track;
 
   selected_paths.clear();
   selected_tracks.clear();
@@ -114,7 +115,14 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize
           break;
         case MussaTrack:
           objid = *ptr++; ++consumed_names;
-          selected_tracks.insert(objid);
+
+          int left = track_container[objid].leftbase(r.left);
+          int right = track_container[objid].rightbase(r.right);
+          // the static_cast should be ok, since basepairs line up on 
+          // integral values
+          //TrackRegion track(objid, left, right); 
+          track.set(objid, left, right);
+          selected_tracks.push_back(track);
         break;
         default:
           cout << "unknown type " << objtype << " ";
@@ -168,7 +176,7 @@ void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
   glFlush();
   glPopMatrix();
   hits = glRenderMode(GL_RENDER);
-  processSelection(hits, selectBuf, select_buf_size);
+  processSelection(hits, selectBuf, select_buf_size, selectedRegion);
 }
 
 float GlSeqBrowser::border() const
@@ -377,6 +385,60 @@ const set<int>& GlSeqBrowser::selectedPaths() const
   return selected_paths;
 }
 
+//! copy sequence from selected track using formating function
+void GlSeqBrowser::copySelectedTracks(std::string& copy_buffer, 
+                                      format_track formatter)
+{
+  copy_buffer.clear();
+
+  for(selected_track_iterator track_i = selected_tracks.begin();
+      track_i != selected_tracks.end();
+      ++track_i)
+  {
+    int track_index = track_i->track_id;
+    if (track_index >= track_container.size()) {
+      // should this be an exception instead?
+      clog << "track " << track_index << " > " << track_container.size() 
+           << endl;
+    } else {
+      // we should be safe
+      const Sequence& seq = track_container[track_index].sequence();
+      copy_buffer += formatter(seq, track_i->left, track_i->right);
+    }
+  }
+  cout << "copy" << endl << copy_buffer << endl;
+}
+
+//! copy sequence from selected tracks as plain sequences
+void GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
+{
+  struct AsString {
+    static string formatter(const Sequence& seq, int left, int right)
+    {
+      stringstream s;
+      s << seq.subseq(left, right-left+1) << std::endl;
+      return s.str();
+    }
+  };
+
+  copySelectedTracks(copy_buffer, AsString::formatter);
+}
+
+//! copy sequence from selected tracks as FASTA sequences
+void GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
+{
+  struct AsFasta {
+    static string formatter(const Sequence& seq, int left, int right)
+    {
+      stringstream s;
+      s << ">" << seq.get_header() << std::endl
+        << seq.subseq(left, right-left+1) << std::endl;
+      return s.str();
+    }
+  };
+  copySelectedTracks(copy_buffer, AsFasta::formatter);
+}
+
 void GlSeqBrowser::centerOnPath(const vector<int>& paths)
 {
   if (paths.size() != track_container.size()) {
index 5a6638d5049fb2552961b892ec5a9db796d00668..109bc5055b0d1d5ed46f3c810de5ddf41cebadaa 100644 (file)
@@ -51,11 +51,12 @@ public:
   //! zoom in to a reasonable sequence view
   double zoomToSequence();
   //! set the current zoom level in (base pairs / pix )
-  /*! its a double as zoom levels of [0.01, 1.0] make nice sequence views
-   */
+  //! its a double as zoom levels of [0.01, 1.0] make nice sequence views
   void setZoom(double zoom_level);
   //! returns the current zoom level as basepairs per pixel
   double zoom() const;
+  //! center the provided path in the current viewport
+  void centerOnPath(const std::vector<int>&);
 
   void setColorMapper(AnnotationColors& cm);
   AnnotationColors& colorMapper();
@@ -76,9 +77,16 @@ public:
   void link(const std::vector<int>& path, const std::vector<bool>& isRC, int length);
   //! returns the index of pathids based on order added by link
   const std::set<int>& selectedPaths() const;
-  //! center the provided path in the current viewport
-  void centerOnPath(const std::vector<int>&);
-
+  //! define our function for formating sequence copy
+  typedef std::string format_track(const Sequence& s, int left, int right);
+  //! copy sequence from selected track using formating function
+  void copySelectedTracks(std::string& copy_buffer, format_track func);
+  //! copy sequence from selected tracks as plain sequences
+  void copySelectedTracksAsString(std::string& copy_buffer);
+  //! copy sequence from selected tracks as FASTA sequences
+  void copySelectedTracksAsFasta(std::string& copy_buffer);
+  
+  
   //! Provide a logical name for a type discriminator for our glName stack
   enum FeatureType { MussaTrack, MussaSegment };
 
@@ -124,6 +132,21 @@ public:
   typedef std::map<segment_key, Segment> pair_segment_map;
   typedef std::vector<pair_segment_map> path_segment_map_vector;
   path_segment_map_vector path_segments;
+
+  struct TrackRegion
+  {
+    GLuint track_id;
+    int left;
+    int right;
+
+    TrackRegion():track_id(0), left(0), right(0) {};
+    TrackRegion(const TrackRegion& o)
+      : track_id(o.track_id), left(o.left), right(o.right) {}
+    TrackRegion(GLuint id, int l, int r)
+      : track_id(id), left(l), right(r) {}
+    void set(GLuint id, int l, int r) { track_id = id; left=l; right=r; };
+  };
+
 private:
   //! recalculate the viewable world
   /*! depending on the size of our canvas, our zoom level and
@@ -135,7 +158,10 @@ private:
   void update_layout();
 
   //! convert opengl selections into the list of paths we should highlight
-  void processSelection(GLuint hits, GLuint buffer[], GLuint bufsize);
+  void processSelection(GLuint hits, 
+                        GLuint buffer[], 
+                        GLuint bufsize, 
+                        const rect<float>& r);
 
   //! master scene drawing function
   /*! draw is broken out for the opengl selection code
@@ -164,14 +190,14 @@ private:
   int pathid;
 
 protected:
-  //! where to draw our box 
+  //! where to draw our box (world coordinates)
   rect<float> selectedRegion;
   //! true if we have a selection
   bool selectedMode;
   //! indicate which paths are selected
   std::set<int> selected_paths;
   //! which track is selected (it only makes sense to have one track selected).
-  std::set<int> selected_tracks;
-
+  std::list<TrackRegion> selected_tracks;
+  typedef std::list<TrackRegion>::iterator selected_track_iterator;
 };
 #endif
index 90b48052d14fe617c49248a7292e02e575f83828..1c4d2a62b0c3e878cbd6c730261d660576b144a5 100644 (file)
@@ -543,9 +543,9 @@ Sequence::get_seq() const
 
 
 std::string
-Sequence::subseq(int start, int end) const
+Sequence::subseq(int start, int count) const
 {
-  return sequence.substr(start, end);
+  return sequence.substr(start, count);
 }
 
 
index 4aa6713c0b720df1afbd29c622fc7815b543812e..41c80cdce00b954a76be18ec8b96e76eacebc39d 100644 (file)
@@ -110,7 +110,7 @@ class Sequence
     void set_seq(const std::string& a_seq);
     const std::string& get_seq() const;
     const char *c_seq() const;
-    std::string subseq(int start, int end) const;
+    std::string subseq(int start, int count) const;
     std::string rev_comp() const;
 
     // string-like functions
index 251648324db3a3dd14f93805e8652872ea37e97e..98051e290bbfda1e51f878be0d7a0a1386e5ad8c 100644 (file)
@@ -87,6 +87,7 @@ BOOST_AUTO_TEST_CASE( sequence_load )
   Sequence s;
   s.load_fasta(seq_path);
   BOOST_CHECK_EQUAL(s.subseq(0, 5), "GGATC"); // first few chars of fasta file
+  BOOST_CHECK_EQUAL(s.subseq(2, 3), "ATC");
   BOOST_CHECK_EQUAL(s.get_header(), "gi|180579|gb|M21487.1|HUMCKMM1 Human "
                                     "muscle creatine kinase gene (CKMM), "
                                     "5' flank");
index 04fe61705d1189dd5a7c7f26cf47ddec25c093d7..b5057cb5f18e47ca5db405d0c39e9094930c1970 100644 (file)
@@ -33,6 +33,7 @@ MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
   threshold(),
   progress_dialog(0),
   aboutAction(0),
+  copySelectedSequenceAsFastaAction(0),
   closeAction(0),
   createNewAnalysisAction(0),
   createSubAnalysisAction(0),
@@ -96,6 +97,8 @@ MussaWindow::~MussaWindow()
   if (progress_dialog != 0) delete progress_dialog;
 
   if (aboutAction != 0) delete aboutAction;
+  if (copySelectedSequenceAsFastaAction != 0) 
+    delete copySelectedSequenceAsFastaAction;
   if (closeAction != 0) delete closeAction;
   if (createNewAnalysisAction != 0) delete createNewAnalysisAction;
   if (createSubAnalysisAction != 0) delete createSubAnalysisAction;
@@ -137,6 +140,11 @@ void MussaWindow::setupActions()
   connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
   aboutAction->setIcon(QIcon(":/icons/info.png"));
 
+  // copy sequence
+  copySelectedSequenceAsFastaAction = new QAction(tr("&Copy As Fasta"), this);
+  connect(copySelectedSequenceAsFastaAction, SIGNAL(triggered()), 
+          &browser, SLOT(copySelectedSequenceAsFasta()));
+
   // add exit
   closeAction = new QAction(tr("&Close"), this);
   closeAction->setStatusTip(tr("Close this window"));
@@ -240,6 +248,9 @@ void MussaWindow::setupMainMenu()
   newMenu->addSeparator();
   newMenu->addAction(closeAction);
 
+  newMenu = menuBar()->addMenu(tr("&Edit"));
+  newMenu->addAction(copySelectedSequenceAsFastaAction);
   newMenu = menuBar()->addMenu(tr("&View"));
   newMenu->addAction(editMotifsAction);
   newMenu->addAction(viewMussaAlignmentAction);
index 42c90a2ee59bd23cb3131881350c4d0aa6488af6..544cea71ea92699cfb207eebe3a0cb5adea8ff08 100644 (file)
@@ -93,6 +93,7 @@ protected:
   QProgressDialog *progress_dialog;
 
   QAction *aboutAction;
+  QAction *copySelectedSequenceAsFastaAction;
   QAction *closeAction;
   QAction *createNewAnalysisAction;
   QAction *createSubAnalysisAction;
index 5e629a7e6de3147de56932493176a44fe0772544..8c3633d0a2f068c3fb5c65254e38428652283160 100644 (file)
@@ -79,6 +79,12 @@ const vector<GlSequence>& SequenceBrowserWidget::sequences() const
   return scrollable_browser.browser().sequences();
 }
 
+void SequenceBrowserWidget::copySelectedSequenceAsFasta()
+{
+  std::string buffer;
+  scrollable_browser.browser().copySelectedTracksAsFasta(buffer);
+}
+
 void SequenceBrowserWidget::clear_links()
 {
   scrollable_browser.browser().clear_links();
index 09a0b4800de26fd9252df762cc5cea3ec2ab1723..63214ca4beb94acb2cc39bdce378e9022c5c24b3 100644 (file)
@@ -43,6 +43,9 @@ public:
   void centerOnPath(const std::vector<int>& paths);
  
 public slots:
+  //! copy selected sequence
+  void copySelectedSequenceAsFasta();
+
   //! set the zoom level of our browser
   void setZoom(double);
   //! zoom to fit the whole scene on the screen