refactored PathScene into SequenceBrowserWidget
authorDiane Trout <diane@caltech.edu>
Fri, 17 Mar 2006 07:16:57 +0000 (07:16 +0000)
committerDiane Trout <diane@caltech.edu>
Fri, 17 Mar 2006 07:16:57 +0000 (07:16 +0000)
The thing that was really hurting my brain was trying to keep track of
which of all the classes needed to actually implement the
SequenceBrowserWidget was actually implementing some function.

SequenceBrowserWidget is now the central control widget that manages
how all of the sub-widgets connect.

I could've tried to simplify how the SequenceBrowserWidget::updatePosition
code worked but it was to hard and I didn't bother.

36 files changed:
Doxyfile
alg/glseqbrowser.cpp [new file with mode: 0644]
alg/glseqbrowser.hpp [new file with mode: 0644]
alg/gltracks.cpp [deleted file]
alg/gltracks.hpp [deleted file]
alg/module.mk
alg/test/module.mk
alg/test/test_annotation_color.cpp
alg/test/test_glseqbrowser.cpp [new file with mode: 0644]
alg/test/test_gltracks.cpp [deleted file]
mussagl.cpp
mussagl.pro
qui/MussaWindow.cpp [new file with mode: 0644]
qui/MussaWindow.hpp [new file with mode: 0644]
qui/PathScene.cpp [deleted file]
qui/PathScene.hpp [deleted file]
qui/PathSidebar.cpp [deleted file]
qui/PathSidebar.hpp [deleted file]
qui/PathWidget.cpp [deleted file]
qui/PathWidget.hpp [deleted file]
qui/PathWindow.cpp [deleted file]
qui/PathWindow.hpp [deleted file]
qui/ScrollableScene.cpp [deleted file]
qui/ScrollableScene.hpp [deleted file]
qui/SequenceDescription.cpp [deleted file]
qui/SequenceDescription.hpp [deleted file]
qui/seqbrowser/ScrollableSequenceBrowser.cpp [new file with mode: 0644]
qui/seqbrowser/ScrollableSequenceBrowser.hpp [new file with mode: 0644]
qui/seqbrowser/SequenceBrowser.cpp [new file with mode: 0644]
qui/seqbrowser/SequenceBrowser.hpp [new file with mode: 0644]
qui/seqbrowser/SequenceBrowserSidebar.cpp [new file with mode: 0644]
qui/seqbrowser/SequenceBrowserSidebar.hpp [new file with mode: 0644]
qui/seqbrowser/SequenceBrowserWidget.cpp [new file with mode: 0644]
qui/seqbrowser/SequenceBrowserWidget.hpp [new file with mode: 0644]
qui/seqbrowser/SequenceDescription.cpp [new file with mode: 0644]
qui/seqbrowser/SequenceDescription.hpp [new file with mode: 0644]

index 3f2a47d1f13fd7dfca5f367d72a13a36e4e08241..ecd33c0605c76227d12b4b5db0d40a77ee9ca40d 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -450,7 +450,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = alg qui .
+INPUT                  = alg qui qui/seqbrowser .
 
 # If the value of the INPUT tag contains directories, you can use the 
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
diff --git a/alg/glseqbrowser.cpp b/alg/glseqbrowser.cpp
new file mode 100644 (file)
index 0000000..cb6b0e3
--- /dev/null
@@ -0,0 +1,459 @@
+#include "alg/glseqbrowser.hpp"
+#include "mussa_exceptions.hpp"
+
+#include <iostream>
+#include <stdexcept>
+
+using namespace std;
+
+GlSeqBrowser::GlSeqBrowser()
+  : border(25),
+    max_ortho(400.0, 0.0, 600.0, 0.0),
+    cur_ortho(max_ortho),
+    viewport_size(600, 400),
+    viewport_center(0),
+    zoom_level(2),
+    color_mapper(),
+    track_container()
+{
+}
+
+GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
+  : border(gt.border),
+    max_ortho(gt.max_ortho),
+    cur_ortho(gt.cur_ortho),
+    viewport_size(gt.viewport_size),
+    viewport_center(gt.viewport_center),
+    zoom_level(gt.zoom_level),
+    color_mapper(gt.color_mapper),
+    track_container(gt.track_container)
+{
+}
+
+void GlSeqBrowser::initializeGL()
+{
+  glEnable(GL_DEPTH_TEST);
+  glClearColor(1.0, 1.0, 1.0, 0.0);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glShadeModel(GL_FLAT);
+}
+
+void GlSeqBrowser::resizeGL(int width, int height)
+{
+  viewport_size.x = width;
+  viewport_size.y = height;
+  glViewport(0, 0, (GLsizei)width, (GLsizei)height);
+  //update_layout();
+}
+
+void GlSeqBrowser::paintGL() const
+{
+  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+  glPushMatrix();
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glOrtho(cur_ortho.left, cur_ortho.right,
+          cur_ortho.bottom, cur_ortho.top,
+          -50.0, 50);
+
+  draw();
+
+  glPopMatrix();
+  glFlush();
+}
+
+void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
+{
+  GLuint *ptr;
+  GLuint names;
+  GLuint consumed_names = 0;
+  float z1;
+  float z2;
+  GLuint objtype;
+  GLuint objid;
+  GLuint path_index = 0;
+  GLuint pair_key_0 = 0;
+  GLuint pair_key_1 = 0;
+
+  selectedPaths.clear();
+  selectedTracks.clear();
+
+  std::cout << "hits = " << hits << std::endl;
+  ptr = (GLuint *) buffer;
+  if (hits > 0)
+    selectedMode = true;
+  for (GLuint i=0; i < hits; ++i)
+  {
+    if ((i + 5) > bufsize) {
+      std::clog << "*** selection overflow***" << std::endl;
+    } else {
+      consumed_names = 0;
+      names = *ptr++;
+      z1 = ((float)*ptr++)/0x7fffffff;
+      z2 = ((float)*ptr++)/0x7fffffff;
+      objtype = *ptr++; ++consumed_names;
+      switch (objtype) {
+        case MussaSegment:
+          path_index = *ptr++; ++consumed_names;
+          pair_key_0 = *ptr++; ++consumed_names;
+          pair_key_1 = *ptr++; ++consumed_names;
+          if (path_index < path_segments.size()) {
+            segment_key k(pair_key_0, pair_key_1);
+            pair_segment_map::iterator psm_i;
+            psm_i = path_segments[path_index].find(k);
+            if (psm_i != path_segments[path_index].end()) {
+              Segment &seg = psm_i->second;
+              selectedPaths.insert(seg.path_ids.begin(), seg.path_ids.end());
+            }
+            // else something else is wrong
+          } else {
+            // something wasn't right
+            clog << "invalid path_index " << path_index 
+                 << " should have been [0,"<<path_segments.size()
+                 << ") " << endl;
+          }
+          break;
+        case MussaTrack:
+          objid = *ptr++; ++consumed_names;
+          selectedTracks.insert(objid);
+        break;
+        default:
+          cout << "unknown type " << objtype << " ";
+          for(; consumed_names < names; ++consumed_names) {
+            cout << consumed_names << "," << *ptr++ << " ";
+          }
+          cout << endl;
+          break;
+      }
+    }
+  }
+}
+
+void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
+{
+  GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
+  GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
+  GLfloat x_left = cur_ortho.left + (left*x_scale);
+  GLfloat x_right = right * x_scale;
+
+  if (top > bottom) {
+    // woah, someone gave us a rectangle with the origin in the lower left
+    int temp = top;
+    bottom = top;
+    top = temp;
+  }
+  // swap the orientation of canvas coordinates
+  GLfloat y_top = cur_ortho.top-(bottom*y_scale);
+  GLfloat y_bottom = cur_ortho.top - top * y_scale;
+  selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
+
+  // hopefully this will make a buffer big enough to receive 
+  // everything being selected
+  //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
+  //const GLuint select_buf_size = 1 + 5 * (pathz_count + sequences.size());
+  const GLuint select_buf_size = 500000;
+  GLuint selectBuf[select_buf_size];
+  glSelectBuffer(select_buf_size, selectBuf);
+  GLint hits;
+
+  (void)glRenderMode(GL_SELECT);
+  glPushMatrix();
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+
+  draw();
+
+  glFlush();
+  glPopMatrix();
+  hits = glRenderMode(GL_RENDER);
+  processSelection(hits, selectBuf, select_buf_size);
+}
+
+float GlSeqBrowser::left() const
+{ 
+  return max_ortho.left; 
+}
+
+float GlSeqBrowser::right() const
+{ 
+  return max_ortho.right; 
+}
+
+void GlSeqBrowser::setViewportCenter(float x)
+{
+  update_viewport(x, zoom_level);
+  viewport_center = x;
+}
+
+float GlSeqBrowser::viewportLeft() const
+{ 
+  return cur_ortho.left; 
+}
+
+float GlSeqBrowser::viewportCenter() const
+{
+  return viewport_center;
+}
+
+float GlSeqBrowser::viewportRight() const
+{ 
+  return cur_ortho.right; 
+}
+
+float GlSeqBrowser::viewportHeight() const
+{
+  return cur_ortho.top - cur_ortho.bottom;
+}
+
+float GlSeqBrowser::viewportWidth() const
+{
+  return cur_ortho.right - cur_ortho.left;
+}
+
+void GlSeqBrowser::setZoom(int new_zoom)
+{
+  update_viewport(viewport_center, new_zoom);
+  zoom_level = new_zoom;
+}
+
+int GlSeqBrowser::zoom() const
+{
+  return zoom_level;
+}
+
+void GlSeqBrowser::setColorMapper(AnnotationColors& cm)
+{
+  color_mapper = cm;
+}
+
+AnnotationColors& GlSeqBrowser::colorMapper()
+{
+  return color_mapper;
+}
+
+void GlSeqBrowser::clear()
+{
+  clear_links();
+  path_segments.clear();
+  track_container.clear();
+}
+
+void GlSeqBrowser::push_sequence(const Sequence &s)
+{
+  GlSequence gs(s, color_mapper);
+  push_sequence(gs);
+}
+
+void GlSeqBrowser::push_sequence(GlSequence &gs)
+{
+  clear_links();
+  pathid = 0;
+  track_container.push_back(gs);
+  update_layout();
+  if (track_container.size() > 1)
+    path_segments.push_back(pair_segment_map());
+}
+
+const std::vector<GlSequence>& GlSeqBrowser::sequences() const
+{
+  return track_container;
+}
+
+void GlSeqBrowser::clear_links()
+{
+  path_segment_map_vector::iterator psmv_i;
+  for(psmv_i = path_segments.begin();
+      psmv_i != path_segments.end();
+      ++psmv_i)
+  {
+    psmv_i->clear();
+  }
+}
+
+void 
+GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int length)
+{
+  if (path.size() < 2) {
+    // should i throw an error instead?
+    return;
+  }
+  if (path.size() != rc.size()) {
+    throw runtime_error("path and reverse compliment must be the same length");
+  }
+  vector<int>::const_iterator path_i = path.begin();
+  vector<bool>::const_iterator rc_i = rc.begin();
+  int track_i = 0;
+  int prev_x = *path_i; ++path_i;
+  bool prev_rc = *rc_i; ++rc_i;
+  while (path_i != path.end() and rc_i != rc.end())
+  {
+    segment_key p(prev_x, *path_i);
+    pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
+    if (found_segment == path_segments[track_i].end()) {
+      // not already found
+      float y1 = track_container[track_i].y();
+            y1 -= track_container[track_i].height()/2;
+      float y2 = track_container[track_i+1].y();
+            y2 += track_container[track_i+1].height()/2;
+            
+      Segment s(prev_x, y1, *path_i, y2, prev_rc);
+      s.path_ids.insert(pathid);
+      path_segments[track_i][p] = s;
+    } else {
+      //found
+      found_segment->second.path_ids.insert(pathid);
+    }
+    prev_x = *path_i;
+    prev_rc = *rc_i;
+    ++track_i;
+    ++path_i;
+    ++rc_i;
+  }
+  // pathid is reset by push_sequence
+  ++pathid;
+}
+
+void GlSeqBrowser::update_viewport(float center, int new_zoom)
+{
+  float max_width = max_ortho.width();
+  // division by zero is a major bummer
+  if (new_zoom < 1) {
+    new_zoom = 1;
+  }
+  float new_max_width = max_width / new_zoom;
+  cur_ortho.left = center-new_max_width;
+  cur_ortho.right = center+new_max_width;
+}
+
+void GlSeqBrowser::update_layout()
+{
+  typedef std::vector<GlSequence>::iterator glseq_itor_type;
+  float available_height = (float)cur_ortho.top - 2 * (float)border;
+  float max_base_pairs = 0;
+  size_t track_count = track_container.size();
+
+  if (track_count > 1) {
+    // we have several sequences
+    float track_spacing = available_height / (track_count-1);
+    float y = available_height + (float)border;
+    for(glseq_itor_type seq_i = track_container.begin();
+        seq_i != track_container.end();
+        ++seq_i, y-=track_spacing)
+    {
+      seq_i->setX(0);
+      seq_i->setY(y);
+      if (seq_i->length() > max_base_pairs)
+        max_base_pairs = seq_i->length();
+    }
+  } else if (track_count == 1) {
+    // center the single track
+    glseq_itor_type seq_i = track_container.begin();
+    seq_i->setX(0);
+    seq_i->setY(viewport_size.x /2);
+    max_base_pairs = seq_i->length();
+  } else {
+    // nothing to do as we're empty
+    return;
+  }
+  max_ortho.right = max_base_pairs + border;
+  max_ortho.left = -border;
+  max_ortho.top = viewport_size.x;
+  max_ortho.bottom = 0;
+  cur_ortho = max_ortho;
+  viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
+}
+
+void GlSeqBrowser::draw() const
+{
+  glMatrixMode(GL_MODELVIEW);
+  glInitNames();
+  glPushName(MussaSegment);
+  draw_segments();
+  glLoadName(MussaTrack);
+  draw_tracks();
+  glPopName();
+  // a selection shouldn't have a glName associated with it
+  draw_selection();
+}
+
+void GlSeqBrowser::draw_selection() const
+{
+  // draw selection box
+  glEnable(GL_BLEND);
+  glDepthMask(GL_FALSE);
+  if (selectedMode) {
+    glColor4f(0.6, 0.6, 0.6, 0.9);
+    glRectf(selectedRegion.left, selectedRegion.top, 
+            selectedRegion.right, selectedRegion.bottom);
+  }
+  glDepthMask(GL_TRUE);
+  glDisable(GL_BLEND);
+}
+
+void GlSeqBrowser::draw_tracks() const
+{
+  for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
+  {
+    glPushName(track_i);
+    track_container[track_i].draw(cur_ortho.left, cur_ortho.right);
+    glPopName();
+  }
+}
+
+void GlSeqBrowser::draw_segments() const
+{
+  glLineWidth(0.1);
+  // each vector contains path_segment_maps of all the connections
+  // between this track and the next
+  path_segment_map_vector::const_iterator psmv_i;
+  for(psmv_i = path_segments.begin();
+      psmv_i != path_segments.end();
+      ++psmv_i)
+  {
+    path_segment_map_vector::difference_type path_index;
+    path_index = psmv_i - path_segments.begin();
+    // these maps contain the pair index (used so we dont keep drawing the
+    // same segment) and the actual segment structure.
+    pair_segment_map::const_iterator psm_i;
+    for(psm_i = psmv_i->begin();
+        psm_i != psmv_i->end();
+        ++psm_i)
+    {
+      // grab the index into our segment map
+      const segment_key& key = psm_i->first;
+      // the second element of our map pair is a segment
+      const Segment &s = psm_i->second;
+      // need to do something so we can detect our selection
+      vector<int> selected;
+      set_intersection(selectedPaths.begin(), selectedPaths.end(),
+                       s.path_ids.begin(), s.path_ids.end(),
+                       back_inserter(selected));
+
+      if (not s.reversed) {
+        if (selectedPaths.size() == 0 or selected.size() > 0) {
+          glColor3f(1.0, 0.0, 0.0);
+        } else {
+          glColor3f(1.0, 0.8, 0.8);
+        }
+      } else { 
+        if (selectedPaths.size() == 0 or selected.size() > 0) {
+          glColor3f(0.0, 0.0, 1.0);
+        } else {
+          glColor3f(0.8, 0.8, 1.0);
+        }
+      }
+      // save the multipart name for our segment
+      glPushName(path_index); glPushName(key.first); glPushName(key.second);
+      glBegin(GL_LINES);
+      glVertex3f(s.start.x, s.start.y, -1);
+      glVertex3f(s.end.x, s.end.y, -1);
+      glEnd();
+      // clear the names
+      glPopName(); glPopName(); glPopName();
+    }
+  }
+}
diff --git a/alg/glseqbrowser.hpp b/alg/glseqbrowser.hpp
new file mode 100644 (file)
index 0000000..fa337a5
--- /dev/null
@@ -0,0 +1,159 @@
+#ifndef _GLTRACKS_H_
+#define _GLTRACKS_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "alg/annotation_colors.hpp"
+#include "alg/sequence.hpp"
+#include "alg/glsequence.hpp"
+
+//! Manage rendering a collection of glSequences
+class GlSeqBrowser
+{
+public:
+  GlSeqBrowser();
+  GlSeqBrowser(const GlSeqBrowser&);
+
+  //! setup the opengl canvas
+  void initializeGL();
+  //! called when our screen canvas is resized
+  void resizeGL(int width, int height);
+  //! render our scene
+  void paintGL() const;
+
+  //! select a region (using canvas coordinates)
+  void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right);
+
+  //! max world left coordinate
+  float left() const;
+  //! max world right coordinate
+  float right() const;
+
+  void setViewportCenter(float x);
+  float viewportLeft() const;
+  float viewportCenter() const;
+  float viewportRight() const;
+  float viewportHeight() const;
+  float viewportWidth() const;
+
+  void setZoom(int zoom_level);
+  int zoom() const;
+
+  void setColorMapper(AnnotationColors& cm);
+  AnnotationColors& colorMapper();
+
+  //! clear our tracks and connections
+  void clear();
+
+  //! add a sequence to the back of our track container
+  void push_sequence(const Sequence &s);
+  //! add a glsequence to the back of our track container
+  void push_sequence(GlSequence &s);
+  //! return our track container
+  const std::vector<GlSequence>& sequences() const;
+
+  //! clear all the line segments between all the sequences
+  void clear_links();
+  //! define a path
+  void link(const std::vector<int>& path, const std::vector<bool>& isRC, int length);
+
+  //! Provide a logical name for a type discriminator for our glName stack
+  enum FeatureType { MussaTrack, MussaSegment };
+
+  //! a useful point class
+  template<class T> struct point {
+    T x;
+    T y;
+
+    point(T x_, T y_):x(x_), y(y_) {}
+  };
+
+  //! a useful rectangle, where 0,0 is in the lower left
+  template<class T> struct rect {
+    T top;
+    T left;
+    T bottom;
+    T right;
+
+    rect() {}
+    rect(T t, T l, T b, T r) : top(t), left(l), bottom(b), right(r) {}
+    T width() { return right - left; }
+    T height() { return top - bottom; }
+  };
+
+  struct Segment
+  {
+    point<float> start;
+    point<float> end;
+    bool reversed;
+    std::set<int> path_ids;
+
+    Segment() : start(0.0, 0.0), end(0.0, 0.0) {}
+    Segment(float x1, float y1, float x2, float y2, bool isRC) 
+      : start(x1, y1), end(x2, y2), reversed(isRC) {}
+  };
+
+  //! data structure holding our line segments
+  /*! the vector is of size track_container.size()-1
+   *  it's indexed by the pair x1, x2 (the two x coordinates between
+   *  the two tracks
+   */
+  typedef std::pair<int, int> segment_key;
+  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;
+private:
+  //! recalculate the viewable world
+  /*! depending on the size of our canvas, our zoom level and
+   *  how far we've been offset
+   */
+  void update_viewport(float center, int new_zoom);
+
+  //! determine where all the tracks should be placed
+  void update_layout();
+
+  //! convert opengl selections into the list of paths we should highlight
+  void processSelection(GLuint hits, GLuint buffer[], GLuint bufsize);
+
+  //! master scene drawing function
+  /*! draw is broken out for the opengl selection code
+   */
+  void draw() const;
+  //! draw glsequence tracks
+  void draw_tracks() const;
+  //! draw line segments between tracks
+  void draw_segments() const;
+  //! draw selection box
+  void draw_selection() const;
+
+  //! number of pixels to reserve around the edges of our canvas
+  const int border;
+  //! the maximum dimensions that our scene is taking up (world coord)
+  rect<float> max_ortho;
+  //! the current viewable region (world coord)
+  rect<float> cur_ortho;
+  //! how many pixels our viewport is (screen coord)
+  point<int> viewport_size;
+  //! the center of our current viewport (world coord) (used for scrollbar)
+  float viewport_center;
+  int zoom_level;
+  AnnotationColors color_mapper;
+  //! container of all the GlSequences loaded into our scene
+  std::vector<GlSequence> track_container;
+  //! counter for each path added to us via connect
+  int pathid;
+
+protected:
+  //! where to draw our box 
+  rect<float> selectedRegion;
+  //! true if we have a selection
+  bool selectedMode;
+  //! indicate which paths are selected
+  std::set<int> selectedPaths;
+  //! which track is selected (it only makes sense to have one track selected).
+  std::set<int> selectedTracks;
+
+};
+#endif
diff --git a/alg/gltracks.cpp b/alg/gltracks.cpp
deleted file mode 100644 (file)
index 1d5e1e1..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-#include "alg/gltracks.hpp"
-#include "mussa_exceptions.hpp"
-
-#include <iostream>
-#include <stdexcept>
-
-using namespace std;
-
-GlTracks::GlTracks()
-  : border(25),
-    max_ortho(400.0, 0.0, 600.0, 0.0),
-    cur_ortho(max_ortho),
-    viewport_size(600, 400),
-    viewport_center(0),
-    zoom_level(2),
-    color_mapper(),
-    track_container()
-{
-}
-
-GlTracks::GlTracks(const GlTracks& gt)
-  : border(gt.border),
-    max_ortho(gt.max_ortho),
-    cur_ortho(gt.cur_ortho),
-    viewport_size(gt.viewport_size),
-    viewport_center(gt.viewport_center),
-    zoom_level(gt.zoom_level),
-    color_mapper(gt.color_mapper),
-    track_container(gt.track_container)
-{
-}
-
-void GlTracks::initializeGL()
-{
-  glEnable(GL_DEPTH_TEST);
-  glClearColor(1.0, 1.0, 1.0, 0.0);
-  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  glShadeModel(GL_FLAT);
-}
-
-void GlTracks::resizeGL(int width, int height)
-{
-  viewport_size.x = width;
-  viewport_size.y = height;
-  glViewport(0, 0, (GLsizei)width, (GLsizei)height);
-  //update_layout();
-}
-
-void GlTracks::paintGL() const
-{
-  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
-
-  glPushMatrix();
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  glOrtho(cur_ortho.left, cur_ortho.right,
-          cur_ortho.bottom, cur_ortho.top,
-          -50.0, 50);
-
-  draw();
-
-  glPopMatrix();
-  glFlush();
-}
-
-void GlTracks::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
-{
-  GLuint *ptr;
-  GLuint names;
-  GLuint consumed_names = 0;
-  float z1;
-  float z2;
-  GLuint objtype;
-  GLuint objid;
-  GLuint path_index = 0;
-  GLuint pair_key_0 = 0;
-  GLuint pair_key_1 = 0;
-
-  selectedPaths.clear();
-  selectedTracks.clear();
-
-  std::cout << "hits = " << hits << std::endl;
-  ptr = (GLuint *) buffer;
-  if (hits > 0)
-    selectedMode = true;
-  for (GLuint i=0; i < hits; ++i)
-  {
-    if ((i + 5) > bufsize) {
-      std::clog << "*** selection overflow***" << std::endl;
-    } else {
-      consumed_names = 0;
-      names = *ptr++;
-      z1 = ((float)*ptr++)/0x7fffffff;
-      z2 = ((float)*ptr++)/0x7fffffff;
-      objtype = *ptr++; ++consumed_names;
-      switch (objtype) {
-        case MussaSegment:
-          path_index = *ptr++; ++consumed_names;
-          pair_key_0 = *ptr++; ++consumed_names;
-          pair_key_1 = *ptr++; ++consumed_names;
-          if (path_index < path_segments.size()) {
-            segment_key k(pair_key_0, pair_key_1);
-            pair_segment_map::iterator psm_i;
-            psm_i = path_segments[path_index].find(k);
-            if (psm_i != path_segments[path_index].end()) {
-              Segment &seg = psm_i->second;
-              selectedPaths.insert(seg.path_ids.begin(), seg.path_ids.end());
-            }
-            // else something else is wrong
-          } else {
-            // something wasn't right
-            clog << "invalid path_index " << path_index 
-                 << " should have been [0,"<<path_segments.size()
-                 << ") " << endl;
-          }
-          break;
-        case MussaTrack:
-          objid = *ptr++; ++consumed_names;
-          selectedTracks.insert(objid);
-        break;
-        default:
-          cout << "unknown type " << objtype << " ";
-          for(; consumed_names < names; ++consumed_names) {
-            cout << consumed_names << "," << *ptr++ << " ";
-          }
-          cout << endl;
-          break;
-      }
-    }
-  }
-}
-
-void GlTracks::selectRegion(int top, int left, int bottom, int right)
-{
-  GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
-  GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
-  GLfloat x_left = cur_ortho.left + (left*x_scale);
-  GLfloat x_right = right * x_scale;
-
-  if (top > bottom) {
-    // woah, someone gave us a rectangle with the origin in the lower left
-    int temp = top;
-    bottom = top;
-    top = temp;
-  }
-  // swap the orientation of canvas coordinates
-  GLfloat y_top = cur_ortho.top-(bottom*y_scale);
-  GLfloat y_bottom = cur_ortho.top - top * y_scale;
-  selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
-
-  // hopefully this will make a buffer big enough to receive 
-  // everything being selected
-  //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
-  //const GLuint select_buf_size = 1 + 5 * (pathz_count + tracks.size());
-  const GLuint select_buf_size = 500000;
-  GLuint selectBuf[select_buf_size];
-  glSelectBuffer(select_buf_size, selectBuf);
-  GLint hits;
-
-  (void)glRenderMode(GL_SELECT);
-  glPushMatrix();
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-
-  draw();
-
-  glFlush();
-  glPopMatrix();
-  hits = glRenderMode(GL_RENDER);
-  processSelection(hits, selectBuf, select_buf_size);
-}
-
-float GlTracks::left() const
-{ 
-  return max_ortho.left; 
-}
-
-float GlTracks::right() const
-{ 
-  return max_ortho.right; 
-}
-
-void GlTracks::setViewportCenter(float x)
-{
-  update_viewport(x, zoom_level);
-  viewport_center = x;
-}
-
-float GlTracks::viewportLeft() const
-{ 
-  return cur_ortho.left; 
-}
-
-float GlTracks::viewportCenter() const
-{
-  return viewport_center;
-}
-
-float GlTracks::viewportRight() const
-{ 
-  return cur_ortho.right; 
-}
-
-float GlTracks::viewportHeight() const
-{
-  return cur_ortho.top - cur_ortho.bottom;
-}
-
-float GlTracks::viewportWidth() const
-{
-  return cur_ortho.right - cur_ortho.left;
-}
-
-void GlTracks::setZoom(int new_zoom)
-{
-  update_viewport(viewport_center, new_zoom);
-  zoom_level = new_zoom;
-}
-
-int GlTracks::zoom() const
-{
-  return zoom_level;
-}
-
-void GlTracks::setColorMapper(AnnotationColors& cm)
-{
-  color_mapper = cm;
-}
-
-AnnotationColors& GlTracks::colorMapper()
-{
-  return color_mapper;
-}
-
-void GlTracks::clear()
-{
-  clear_links();
-  path_segments.clear();
-  track_container.clear();
-}
-
-void GlTracks::push_sequence(const Sequence &s)
-{
-  GlSequence gs(s, color_mapper);
-  push_sequence(gs);
-}
-
-void GlTracks::push_sequence(GlSequence &gs)
-{
-  clear_links();
-  pathid = 0;
-  track_container.push_back(gs);
-  update_layout();
-  if (track_container.size() > 1)
-    path_segments.push_back(pair_segment_map());
-}
-
-const std::vector<GlSequence>& GlTracks::tracks() const
-{
-  return track_container;
-}
-
-void GlTracks::clear_links()
-{
-  path_segment_map_vector::iterator psmv_i;
-  for(psmv_i = path_segments.begin();
-      psmv_i != path_segments.end();
-      ++psmv_i)
-  {
-    psmv_i->clear();
-  }
-}
-
-void 
-GlTracks::link(vector<int> path, vector<bool> rc, int length)
-{
-  if (path.size() < 2) {
-    // should i throw an error instead?
-    return;
-  }
-  if (path.size() != rc.size()) {
-    throw runtime_error("path and reverse compliment must be the same length");
-  }
-  vector<int>::iterator path_i = path.begin();
-  vector<bool>::iterator rc_i = rc.begin();
-  int track_i = 0;
-  int prev_x = *path_i; ++path_i;
-  bool prev_rc = *rc_i; ++rc_i;
-  while (path_i != path.end() and rc_i != rc.end())
-  {
-    segment_key p(prev_x, *path_i);
-    pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
-    if (found_segment == path_segments[track_i].end()) {
-      // not already found
-      float y1 = track_container[track_i].y();
-            y1 -= track_container[track_i].height()/2;
-      float y2 = track_container[track_i+1].y();
-            y2 += track_container[track_i+1].height()/2;
-            
-      Segment s(prev_x, y1, *path_i, y2, prev_rc);
-      s.path_ids.insert(pathid);
-      path_segments[track_i][p] = s;
-    } else {
-      //found
-      found_segment->second.path_ids.insert(pathid);
-    }
-    prev_x = *path_i;
-    prev_rc = *rc_i;
-    ++track_i;
-    ++path_i;
-    ++rc_i;
-  }
-  // pathid is reset by push_sequence
-  ++pathid;
-}
-
-void GlTracks::update_viewport(float center, int new_zoom)
-{
-  float max_width = max_ortho.width();
-  // division by zero is a major bummer
-  if (new_zoom < 1) {
-    new_zoom = 1;
-  }
-  float new_max_width = max_width / new_zoom;
-  cur_ortho.left = center-new_max_width;
-  cur_ortho.right = center+new_max_width;
-}
-
-void GlTracks::update_layout()
-{
-  typedef std::vector<GlSequence>::iterator glseq_itor_type;
-  float available_height = (float)cur_ortho.top - 2 * (float)border;
-  float max_base_pairs = 0;
-  size_t track_count = track_container.size();
-
-  if (track_count > 1) {
-    // we have several tracks
-    float track_spacing = available_height / (track_count-1);
-    float y = available_height + (float)border;
-    for(glseq_itor_type seq_i = track_container.begin();
-        seq_i != track_container.end();
-        ++seq_i, y-=track_spacing)
-    {
-      seq_i->setX(0);
-      seq_i->setY(y);
-      if (seq_i->length() > max_base_pairs)
-        max_base_pairs = seq_i->length();
-    }
-  } else if (track_count == 1) {
-    // center the single track
-    glseq_itor_type seq_i = track_container.begin();
-    seq_i->setX(0);
-    seq_i->setY(viewport_size.x /2);
-    max_base_pairs = seq_i->length();
-  } else {
-    // nothing to do as we're empty
-    return;
-  }
-  max_ortho.right = max_base_pairs + border;
-  max_ortho.left = -border;
-  max_ortho.top = viewport_size.x;
-  max_ortho.bottom = 0;
-  cur_ortho = max_ortho;
-  viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
-}
-
-void GlTracks::draw() const
-{
-  glMatrixMode(GL_MODELVIEW);
-  glInitNames();
-  glPushName(MussaSegment);
-  draw_segments();
-  glLoadName(MussaTrack);
-  draw_tracks();
-  glPopName();
-  // a selection shouldn't have a glName associated with it
-  draw_selection();
-}
-
-void GlTracks::draw_selection() const
-{
-  // draw selection box
-  glEnable(GL_BLEND);
-  glDepthMask(GL_FALSE);
-  if (selectedMode) {
-    glColor4f(0.6, 0.6, 0.6, 0.9);
-    glRectf(selectedRegion.left, selectedRegion.top, 
-            selectedRegion.right, selectedRegion.bottom);
-  }
-  glDepthMask(GL_TRUE);
-  glDisable(GL_BLEND);
-}
-
-void GlTracks::draw_tracks() const
-{
-  for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
-  {
-    glPushName(track_i);
-    track_container[track_i].draw(cur_ortho.left, cur_ortho.right);
-    glPopName();
-  }
-}
-
-void GlTracks::draw_segments() const
-{
-  glLineWidth(0.1);
-  // each vector contains path_segment_maps of all the connections
-  // between this track and the next
-  path_segment_map_vector::const_iterator psmv_i;
-  for(psmv_i = path_segments.begin();
-      psmv_i != path_segments.end();
-      ++psmv_i)
-  {
-    path_segment_map_vector::difference_type path_index;
-    path_index = psmv_i - path_segments.begin();
-    // these maps contain the pair index (used so we dont keep drawing the
-    // same segment) and the actual segment structure.
-    pair_segment_map::const_iterator psm_i;
-    for(psm_i = psmv_i->begin();
-        psm_i != psmv_i->end();
-        ++psm_i)
-    {
-      // grab the index into our segment map
-      const segment_key& key = psm_i->first;
-      // the second element of our map pair is a segment
-      const Segment &s = psm_i->second;
-      // need to do something so we can detect our selection
-      vector<int> selected;
-      set_intersection(selectedPaths.begin(), selectedPaths.end(),
-                       s.path_ids.begin(), s.path_ids.end(),
-                       back_inserter(selected));
-
-      if (not s.reversed) {
-        if (selectedPaths.size() == 0 or selected.size() > 0) {
-          glColor3f(1.0, 0.0, 0.0);
-        } else {
-          glColor3f(1.0, 0.8, 0.8);
-        }
-      } else { 
-        if (selectedPaths.size() == 0 or selected.size() > 0) {
-          glColor3f(0.0, 0.0, 1.0);
-        } else {
-          glColor3f(0.8, 0.8, 1.0);
-        }
-      }
-      // save the multipart name for our segment
-      glPushName(path_index); glPushName(key.first); glPushName(key.second);
-      glBegin(GL_LINES);
-      glVertex3f(s.start.x, s.start.y, -1);
-      glVertex3f(s.end.x, s.end.y, -1);
-      glEnd();
-      // clear the names
-      glPopName(); glPopName(); glPopName();
-    }
-  }
-}
diff --git a/alg/gltracks.hpp b/alg/gltracks.hpp
deleted file mode 100644 (file)
index 2a88aff..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#ifndef _GLTRACKS_H_
-#define _GLTRACKS_H_
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "alg/annotation_colors.hpp"
-#include "alg/sequence.hpp"
-#include "alg/glsequence.hpp"
-
-//! Manage rendering a collection of glSequences
-class GlTracks
-{
-public:
-  GlTracks();
-  GlTracks(const GlTracks&);
-
-  //! setup the opengl canvas
-  void initializeGL();
-  //! called when our screen canvas is resized
-  void resizeGL(int width, int height);
-  //! render our scene
-  void paintGL() const;
-
-  //! select a region (using canvas coordinates)
-  void GlTracks::selectRegion(int top, int left, int bottom, int right);
-
-  //! max world left coordinate
-  float left() const;
-  //! max world right coordinate
-  float right() const;
-
-  void setViewportCenter(float x);
-  float viewportLeft() const;
-  float viewportCenter() const;
-  float viewportRight() const;
-  float viewportHeight() const;
-  float viewportWidth() const;
-
-  void setZoom(int zoom_level);
-  int zoom() const;
-
-  void setColorMapper(AnnotationColors& cm);
-  AnnotationColors& colorMapper();
-
-  //! clear our tracks and connections
-  void clear();
-
-  //! add a sequence to the back of our track container
-  void push_sequence(const Sequence &s);
-  //! add a glsequence to the back of our track container
-  void push_sequence(GlSequence &s);
-  //! return our track container
-  const std::vector<GlSequence>& tracks() const;
-
-  //! clear all the line segments between all the sequences
-  void clear_links();
-  //! define a path
-  void link(std::vector<int> path, std::vector<bool> isRC, int length);
-
-  //! Provide a logical name for a type discriminator for our glName stack
-  enum FeatureType { MussaTrack, MussaSegment };
-
-  //! a useful point class
-  template<class T> struct point {
-    T x;
-    T y;
-
-    point(T x_, T y_):x(x_), y(y_) {}
-  };
-
-  //! a useful rectangle, where 0,0 is in the lower left
-  template<class T> struct rect {
-    T top;
-    T left;
-    T bottom;
-    T right;
-
-    rect() {}
-    rect(T t, T l, T b, T r) : top(t), left(l), bottom(b), right(r) {}
-    T width() { return right - left; }
-    T height() { return top - bottom; }
-  };
-
-  struct Segment
-  {
-    point<float> start;
-    point<float> end;
-    bool reversed;
-    std::set<int> path_ids;
-
-    Segment() : start(0.0, 0.0), end(0.0, 0.0) {}
-    Segment(float x1, float y1, float x2, float y2, bool isRC) 
-      : start(x1, y1), end(x2, y2), reversed(isRC) {}
-  };
-
-  //! data structure holding our line segments
-  /*! the vector is of size track_container.size()-1
-   *  it's indexed by the pair x1, x2 (the two x coordinates between
-   *  the two tracks
-   */
-  typedef std::pair<int, int> segment_key;
-  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;
-private:
-  //! recalculate the viewable world
-  /*! depending on the size of our canvas, our zoom level and
-   *  how far we've been offset
-   */
-  void update_viewport(float center, int new_zoom);
-
-  //! determine where all the tracks should be placed
-  void update_layout();
-
-  //! convert opengl selections into the list of paths we should highlight
-  void processSelection(GLuint hits, GLuint buffer[], GLuint bufsize);
-
-  //! master scene drawing function
-  /*! draw is broken out for the opengl selection code
-   */
-  void draw() const;
-  //! draw glsequence tracks
-  void draw_tracks() const;
-  //! draw line segments between tracks
-  void draw_segments() const;
-  //! draw selection box
-  void draw_selection() const;
-
-  //! number of pixels to reserve around the edges of our canvas
-  const int border;
-  //! the maximum dimensions that our scene is taking up (world coord)
-  rect<float> max_ortho;
-  //! the current viewable region (world coord)
-  rect<float> cur_ortho;
-  //! how many pixels our viewport is (screen coord)
-  point<int> viewport_size;
-  //! the center of our current viewport (world coord) (used for scrollbar)
-  float viewport_center;
-  int zoom_level;
-  AnnotationColors color_mapper;
-  //! container of all the GlSequences loaded into our scene
-  std::vector<GlSequence> track_container;
-  //! counter for each path added to us via connect
-  int pathid;
-
-protected:
-  //! where to draw our box 
-  rect<float> selectedRegion;
-  //! true if we have a selection
-  bool selectedMode;
-  //! indicate which paths are selected
-  std::set<int> selectedPaths;
-  //! which track is selected (it only makes sense to have one track selected).
-  std::set<int> selectedTracks;
-
-};
-#endif
index 4a215ed36ba04ba198b71542e9312182a4468075..105f2021fdf736b19f248d04e8d84e9087437ea4 100644 (file)
@@ -16,7 +16,7 @@ MUSSA_ALG_SRC := $(addprefix $(CURDIR), $(SOURCES.cpp))
 MUSSA_ALG_OBJ := $(MUSSA_ALG_SRC:.cpp=$(OBJEXT))
 
 GLSOURCES.cpp := glsequence.cpp  \
-                 gltracks.cpp
+                 glseqbrowser.cpp
 MUSSA_ALG_GL_SRC := $(addprefix $(CURDIR), $(GLSOURCES.cpp))
 MUSSA_ALG_GL_OBJ := $(MUSSA_ALG_GL_SRC:.cpp=$(OBJEXT))
 
index 63bf40824b8dc6ed9f97ab3c544ef863c01346b7..dae6b681aeadae49f10771edfa8bbc91b52a25a7 100644 (file)
@@ -4,7 +4,7 @@ SOURCES.cpp := test_annotation_color.cpp \
                test_conserved_path.cpp \
                test_flp.cpp \
                test_glsequence.cpp \
-               test_gltracks.cpp \
+               test_glseqbrowser.cpp \
                test_color.cpp \
                test_main.cpp \
                test_mussa.cpp \
index 2e2a56ebaca41f5f7b170b7be2db65bd4f2eeeba..4cde0ddf463b400d6e770da969c46fcb06287af6 100644 (file)
@@ -41,3 +41,22 @@ BOOST_AUTO_TEST_CASE( simple_annot_colors )
   BOOST_CHECK_EQUAL( ac.lookup("bleem", "a"), black);
 }
 
+// can we assign a color?
+BOOST_AUTO_TEST_CASE( annotation_color_assignment )
+{
+  Color black(0.0, 0.0, 0.0);
+  Color white(1.0, 1.0, 1.0);
+  Color red(1.0, 0.0, 0.0);
+
+  AnnotationColors ac1;
+  ac1.setColor(white);
+  ac1.appendTypeColor("bleem", black);
+  ac1.appendInstanceColor("bleem", "a", red);
+
+  AnnotationColors ac2;
+  BOOST_CHECK_EQUAL( ac2.color(), black );
+  ac2 = ac1;
+  BOOST_CHECK_EQUAL( ac2.color(), white );
+  BOOST_CHECK_EQUAL( ac2.typeColor("bleem"), ac1.typeColor("bleem") );
+  BOOST_CHECK_EQUAL( ac2.lookup("bleem","a"), ac1.lookup("bleem","a") );
+}
diff --git a/alg/test/test_glseqbrowser.cpp b/alg/test/test_glseqbrowser.cpp
new file mode 100644 (file)
index 0000000..ba4fcae
--- /dev/null
@@ -0,0 +1,47 @@
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign;
+
+#include <string>
+#include <list>
+#include <vector>
+
+#include "alg/annotation_colors.hpp"
+#include "alg/glseqbrowser.hpp"
+#include "alg/sequence.hpp"
+
+using namespace std;
+
+BOOST_AUTO_TEST_CASE ( gltracks_connect )
+{
+  string s0("AAGGCCTT");
+  string s1("TTGGCCAA");
+  string s2("GATTACAA");
+  Sequence seq0(s0);
+  Sequence seq1(s1);
+  Sequence seq2(s2);
+
+  GlSeqBrowser gt;
+  gt.push_sequence(seq0);
+  gt.push_sequence(seq1);
+  gt.push_sequence(seq2);
+
+  // make up some sample data
+  vector<bool> rc;
+  rc += false, false, false;
+  vector<int> path;
+                path += 1,1,1; gt.link(path, rc, 1);
+  path.clear(); path += 1,1,3; gt.link(path, rc, 1); 
+  path.clear(); path += 2,3,3; gt.link(path, rc, 1); 
+  path.clear(); path += 3,3,3; gt.link(path, rc, 1);
+
+  BOOST_CHECK_EQUAL( gt.path_segments.size(), 2 );
+  GlSeqBrowser::segment_key p(1, 1);
+  GlSeqBrowser::pair_segment_map::iterator psm_i = gt.path_segments[0].find(p);
+  BOOST_CHECK( psm_i != gt.path_segments[0].end() );
+  BOOST_CHECK_EQUAL( psm_i->second.path_ids.size(), 2 );
+
+  gt.clear();
+  BOOST_CHECK_EQUAL( gt.path_segments.size(), 0 );
+}
+
diff --git a/alg/test/test_gltracks.cpp b/alg/test/test_gltracks.cpp
deleted file mode 100644 (file)
index 58b91fc..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <boost/test/auto_unit_test.hpp>
-#include <boost/assign/std/vector.hpp>
-using namespace boost::assign;
-
-#include <string>
-#include <list>
-#include <vector>
-
-#include "alg/annotation_colors.hpp"
-#include "alg/gltracks.hpp"
-#include "alg/sequence.hpp"
-
-using namespace std;
-
-BOOST_AUTO_TEST_CASE ( gltracks_connect )
-{
-  string s0("AAGGCCTT");
-  string s1("TTGGCCAA");
-  string s2("GATTACAA");
-  Sequence seq0(s0);
-  Sequence seq1(s1);
-  Sequence seq2(s2);
-
-  GlTracks gt;
-  gt.push_sequence(seq0);
-  gt.push_sequence(seq1);
-  gt.push_sequence(seq2);
-
-  // make up some sample data
-  vector<bool> rc;
-  rc += false, false, false;
-  vector<int> path;
-                path += 1,1,1; gt.link(path, rc, 1);
-  path.clear(); path += 1,1,3; gt.link(path, rc, 1); 
-  path.clear(); path += 2,3,3; gt.link(path, rc, 1); 
-  path.clear(); path += 3,3,3; gt.link(path, rc, 1);
-
-  BOOST_CHECK_EQUAL( gt.path_segments.size(), 2 );
-  GlTracks::segment_key p(1, 1);
-  GlTracks::pair_segment_map::iterator psm_i = gt.path_segments[0].find(p);
-  BOOST_CHECK( psm_i != gt.path_segments[0].end() );
-  BOOST_CHECK_EQUAL( psm_i->second.path_ids.size(), 2 );
-
-  gt.clear();
-  BOOST_CHECK_EQUAL( gt.path_segments.size(), 0 );
-}
-
index 1447b7d250d5ae177dcba911abd4236a0a0076a8..008b888fbcca15dc70da33367248d5b9fc296dbd 100644 (file)
@@ -1,6 +1,6 @@
 #include <QApplication>
 
-#include "qui/PathWindow.hpp"
+#include "qui/MussaWindow.hpp"
 #include "alg/parse_options.hpp"
 
 int main(int argc, char **argv)
@@ -12,7 +12,7 @@ int main(int argc, char **argv)
     return 1;
   }
 
-  PathWindow win(analysis);
+  MussaWindow win(analysis);
   win.show();
   app.exec();
   return 0;
index ae1cade64d289183f9b00a32ef5640c52c9c84f3..8c0633564458b588b6a85866149a976d1664f341 100644 (file)
@@ -12,42 +12,42 @@ INCLUDEPATH += . alg qui
 
 # Input
 HEADERS += mussa_exceptions.hpp \
-           qui/PathWidget.hpp \
-           qui/PathWindow.hpp \
-           qui/PathScene.hpp \
-           qui/PathSidebar.hpp \
-           qui/ScrollableScene.hpp \
-           qui/SequenceDescription.hpp \
+           qui/MussaWindow.hpp \
            qui/ThresholdWidget.hpp \
            qui/ImageScaler.hpp \
            qui/ImageSaveDialog.hpp \
+           qui/seqbrowser/SequenceBrowserWidget.hpp \
+           qui/seqbrowser/SequenceBrowser.hpp \
+           qui/seqbrowser/SequenceBrowserSidebar.hpp \
+           qui/seqbrowser/ScrollableSequenceBrowser.hpp \
+           qui/seqbrowser/SequenceDescription.hpp \
            alg/annotation_colors.hpp \
            alg/color.hpp \
            alg/conserved_path.hpp \
            alg/flp.hpp \
            alg/glsequence.hpp \
-           alg/gltracks.hpp \
+           alg/glseqbrowser.hpp \
            alg/mussa.hpp \
            alg/nway_paths.hpp \
            alg/parse_options.hpp \
            alg/sequence.hpp
 SOURCES += mussagl.cpp \
-           qui/PathWidget.cpp \
-           qui/PathWindow.cpp \
-           qui/PathScene.cpp \
-           qui/PathSidebar.cpp \
-           qui/ScrollableScene.cpp \
-           qui/SequenceDescription.cpp \
+           qui/MussaWindow.cpp \
            qui/ThresholdWidget.cpp \
            qui/ImageScaler.cpp \
            qui/ImageSaveDialog.cpp \
+           qui/seqbrowser/SequenceBrowserWidget.cpp \
+           qui/seqbrowser/SequenceBrowser.cpp \
+           qui/seqbrowser/SequenceBrowserSidebar.cpp \
+           qui/seqbrowser/ScrollableSequenceBrowser.cpp \
+           qui/seqbrowser/SequenceDescription.cpp \
            alg/annotation_colors.cpp \
            alg/color.cpp \
            alg/conserved_path.cpp \
            alg/flp.cpp \
            alg/flp_seqcomp.cpp \
            alg/glsequence.cpp \
-           alg/gltracks.cpp \
+           alg/glseqbrowser.cpp \
            alg/mussa.cpp \
            alg/nway_entropy.cpp \
            alg/nway_other.cpp \
diff --git a/qui/MussaWindow.cpp b/qui/MussaWindow.cpp
new file mode 100644 (file)
index 0000000..4822d31
--- /dev/null
@@ -0,0 +1,343 @@
+#include <QAction>
+#include <QDir>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QIcon>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QScrollBar>
+#include <QStatusBar>
+#include <QString>
+#include <QWhatsThis>
+
+#include "qui/MussaWindow.hpp"
+#include "mussa_exceptions.hpp"
+
+#include <iostream>
+
+using namespace std;
+
+MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) :
+  QMainWindow(parent),
+  analysis(analysis_),
+  browser(this),
+  mussaViewTB("Path Views"),
+  zoomBox(),
+  threshold(),
+  closeAction(0) // initialize one of the pointers to null as a saftey flag
+{
+  setupActions();
+  setupMainMenu();
+
+  //This next setWhatsThis function prevents
+  // a segfault when using WhatsThis feature with 
+  // opengl widget.
+  //scene->setWhatsThis(tr("Mussa in OpenGL!"));
+  setCentralWidget(&browser);
+
+  mussaViewTB.addAction(toggleMotifsAction);
+
+  zoomBox.setWhatsThis("zoom magnification factor");
+  zoomBox.setRange(2,1000);
+  mussaViewTB.addWidget(&zoomBox);
+  connect(&zoomBox, SIGNAL(valueChanged(int)), 
+          &browser, SLOT(setZoom(int)));
+  
+  threshold.setRange(19, 30);
+  threshold.setThreshold(19);
+  //scene->setClipPlane(20);
+  // FIXME: for when we get the paths drawn at the appropriate depth
+  //connect(&threshold, SIGNAL(thresholdChanged(int)),
+  //        this, SLOT(setClipPlane(int)));
+  connect(&threshold, SIGNAL(thresholdChanged(int)),
+          this, SLOT(setSoftThreshold(int)));
+  mussaViewTB.addWidget(&threshold);
+
+  addToolBar(&mussaViewTB);
+
+  statusBar()->showMessage("Welcome to mussa", 2000);
+  updateAnalysis();
+}
+
+void MussaWindow::setupActions()
+{
+  // we really don't want to run this more than once.
+  assert (closeAction == 0);
+
+  // the ever popular about box
+  aboutAction = new QAction(tr("&About"), this);
+  connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
+  aboutAction->setIcon(QIcon("icons/info.png"));
+
+  // add exit
+  closeAction = new QAction(tr("&Close"), this);
+  closeAction->setStatusTip(tr("Close this window"));
+  connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
+  closeAction->setIcon(QIcon("icons/exit.png"));
+  
+  createNewAnalysisAction = new QAction(tr("Define Analysis"), this);
+  connect(createNewAnalysisAction, SIGNAL(triggered()), 
+          this, SLOT(createNewAnalysis()));
+  createNewAnalysisAction->setIcon(QIcon("icons/filenew.png"));
+  
+  createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this);
+  connect(createSubAnalysisAction, SIGNAL(triggered()), 
+          this, SLOT(createSubAnalysis()));
+
+  loadMotifListAction = new QAction(tr("Load Motif List"), this);
+  connect(loadMotifListAction, SIGNAL(triggered()), 
+          this, SLOT(loadMotifList()));
+  
+  loadMupaAction = new QAction(tr("Load Mussa Parameters"), this);
+  connect(loadMupaAction, SIGNAL(triggered()), 
+          this, SLOT(loadMupa()));
+
+  loadSavedAnalysisAction = new QAction(tr("Load &Analysis"), this);
+  connect(loadSavedAnalysisAction, SIGNAL(triggered()), 
+          this, SLOT(loadSavedAnalysis()));
+  loadSavedAnalysisAction->setIcon(QIcon("icons/fileopen.png"));
+
+  saveMotifListAction = new QAction(tr("Save Motifs"), this);
+  connect(saveMotifListAction, SIGNAL(triggered()), 
+          this, SLOT(saveMotifList()));
+  saveMotifListAction->setIcon(QIcon("icons/filesave.png"));
+
+  showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
+  connect(showMussaViewToolbarAction, SIGNAL(triggered()), 
+          this, SLOT(showMussaToolbar()));
+  showMussaViewToolbarAction->setCheckable(true);
+  showMussaViewToolbarAction->setChecked(true);
+
+  toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
+  connect(toggleMotifsAction, SIGNAL(triggered()), 
+          this, SLOT(toggleMotifs()));
+  toggleMotifsAction->setCheckable(true);
+  toggleMotifsAction->setIcon(QIcon("icons/motif_icon.png"));
+  toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
+                                  "You can load motif annotations via "
+                                  "'File->Load Motif List' menu option."));
+
+  whatsThisAction = QWhatsThis::createAction(this);
+  whatsThisAction->setIcon(QIcon("icons/help.png"));
+
+  //Save pixel map action
+  saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
+  connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
+         &browser, SLOT(promptSaveBrowserPixmap()));
+  saveBrowserPixmapAction->setIcon(QIcon("icons/image2.png"));
+}
+
+void MussaWindow::setupMainMenu()
+{
+  // we need to run setupActions first
+  assert (closeAction != 0);
+  
+  QMenu *newMenu;
+  newMenu = menuBar()->addMenu(tr("&File"));
+  newMenu->addAction(createNewAnalysisAction);
+  newMenu->addAction(loadMupaAction);
+  newMenu->addAction(loadSavedAnalysisAction);
+  newMenu->addAction(createSubAnalysisAction);
+  newMenu->addSeparator();
+  newMenu->addAction(loadMotifListAction);
+  newMenu->addAction(saveMotifListAction);
+  newMenu->addSeparator();
+  newMenu->addAction(saveBrowserPixmapAction);
+  newMenu->addSeparator();
+  newMenu->addAction(closeAction);
+
+  newMenu = menuBar()->addMenu(tr("&View"));
+  newMenu->addAction(showMussaViewToolbarAction);
+
+  newMenu = menuBar()->addMenu(tr("&Help"));
+  newMenu->addAction(whatsThisAction);
+  newMenu->addSeparator();
+  newMenu->addAction(aboutAction);
+}
+  
+void MussaWindow::about()
+{
+  QMessageBox::about(this, tr("About mussa"),
+      tr("Welcome to Multiple Species Sequence Analysis\n"
+         "(c) 2005-2006 California Institute of Technology\n"
+         "Tristan De Buysscher, Diane Trout\n"));
+}
+
+void MussaWindow::createNewAnalysis()
+{
+  NotImplementedBox();
+}
+
+void MussaWindow::createSubAnalysis()
+{
+  NotImplementedBox();
+}
+
+
+void MussaWindow::loadMotifList()
+{
+  QString caption("Load a motif list");
+  QString filter("Motif list(*.txt *.mtl)");
+  QString path = QFileDialog::getOpenFileName(this,
+                                                   caption, 
+                                                   QDir::currentPath(),
+                                                   filter);
+  // user hit cancel?
+  if (path.isNull()) 
+    return;
+  // try to load safely
+  try {
+    analysis->load_motifs(path.toStdString());
+  } catch (runtime_error e) {
+    QString msg("Unable to load ");
+    msg += path;
+    msg += "\n";
+    msg += e.what();
+    QMessageBox::warning(this, "Load Motifs", msg);
+  }
+  assert (analysis != 0);
+}
+
+void MussaWindow::saveMotifList()
+{
+  NotImplementedBox();
+}
+
+void MussaWindow::loadMupa()
+{
+  QString caption("Load a mussa parameter file");
+  QString filter("Mussa Parameters (*.mupa)");
+  QString mupa_path = QFileDialog::getOpenFileName(this,
+                                                   caption, 
+                                                   QDir::currentPath(),
+                                                   filter);
+  // user hit cancel?
+  if (mupa_path.isNull()) 
+    return;
+  // try to load safely
+  try {
+    Mussa *m = new Mussa;
+    m->load_mupa_file(mupa_path.toStdString());
+    m->analyze(0, 0, Mussa::TransitiveNway, 0.0);
+    // only switch mussas if we loaded without error
+    delete analysis;
+    analysis = m;
+    updateAnalysis();
+  } catch (mussa_load_error e) {
+    QString msg("Unable to load ");
+    msg += mupa_path;
+    msg += "\n";
+    msg += e.what();
+    QMessageBox::warning(this, "Load Parameter", msg);
+  }
+  assert (analysis != 0);
+}
+
+void MussaWindow::loadSavedAnalysis()
+{
+  QString caption("Load a previously run analysis");
+  QString muway_dir = QFileDialog::getExistingDirectory(this,
+                                                        caption, 
+                                                        QDir::currentPath());
+  // user hit cancel?
+  if (muway_dir.isNull()) 
+    return;
+  // try to safely load
+  try {
+    Mussa *m = new Mussa;
+    m->load(muway_dir.toStdString());
+    // only switch mussas if we loaded without error
+    delete analysis;
+    analysis = m;
+    updateAnalysis();
+  } catch (mussa_load_error e) {
+    QString msg("Unable to load ");
+    msg += muway_dir;
+    msg += "\n";
+    msg += e.what();
+    QMessageBox::warning(this, "Load Parameter", msg);
+  }
+  assert (analysis != 0);
+}
+
+void MussaWindow::setSoftThreshold(int threshold)
+{
+  if (analysis->get_soft_thres() != threshold) {
+    analysis->set_soft_thres(threshold);
+    analysis->nway();
+    updateLinks();
+    update();
+  }
+}
+
+void MussaWindow::showMussaToolbar()
+{
+  if (mussaViewTB.isVisible())
+    mussaViewTB.hide();
+  else
+    mussaViewTB.show();
+}
+
+void MussaWindow::toggleMotifs()
+{
+  NotImplementedBox();
+}
+
+void MussaWindow::NotImplementedBox()
+{
+  QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
+}      
+
+void MussaWindow::updateAnalysis()
+{
+  cout << "analysis updated" << endl;
+  browser.clear();
+  const vector<Sequence>& seqs = analysis->sequences();
+  browser.setSequences(seqs, analysis->colorMapper());
+  updateLinks();
+}
+
+void MussaWindow::updateLinks()
+{
+  browser.clear_links();
+  bool reversed = false;
+  const NwayPaths& nway = analysis->paths();
+
+  typedef list<ExtendedConservedPath> conserved_paths;
+  typedef conserved_paths::const_iterator const_conserved_paths_itor;
+  for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
+      path_itor != nway.refined_pathz.end();
+      ++path_itor)
+  {
+    // since we were drawing to the start of a window, and opengl lines
+    // are centered around the two connecting points our lines were slightly
+    // offset. the idea of window_offset is to adjust them to the
+    // right for forward compliment or left for reverse compliment
+    // FIXME: figure out how to unit test these computations
+    //GLfloat window_offset = (path_itor->window_size)/2.0;
+
+    size_t track_len = path_itor->track_indexes.size();
+    vector<int> normalized_path;
+    normalized_path.reserve(track_len);
+    vector<bool> rc_flags(false, track_len);
+    for (size_t track_i=0; track_i != track_len; ++track_i)
+    {
+      int x = path_itor->track_indexes[track_i];
+      // at some point when we modify the pathz data structure to keep
+      // track of the score we can put grab the depth here.
+      //
+      // are we reverse complimented?
+      if ( x>=0) {
+        reversed = false;
+      } else {
+        reversed = true;
+        x = -x; // make positive
+      }
+      normalized_path.push_back(x);
+      rc_flags.push_back(reversed);
+    }
+    browser.link(normalized_path, rc_flags, path_itor->window_size);
+  }
+  browser.update();
+}
+
diff --git a/qui/MussaWindow.hpp b/qui/MussaWindow.hpp
new file mode 100644 (file)
index 0000000..0ba819c
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef _MUSSAWINDOW_H_
+#define _MUSSAWINDOW_H_
+
+#include <QMainWindow>
+#include <QPixmap>
+#include <QSpinBox>
+#include <QToolBar>
+
+#include "qui/seqbrowser/SequenceBrowserWidget.hpp"
+#include "qui/ThresholdWidget.hpp"
+
+class QAction;
+class Mussa;
+
+class MussaWindow : public QMainWindow
+{
+  Q_OBJECT
+
+public: 
+  MussaWindow(Mussa* analysis=0, QWidget *parent=0);
+
+public slots:
+  //! display an about box, contemplating the politics of the author list
+  void about();
+
+  //! \defgroup AnalysisFunctions Components related to running analyses
+  //\@{
+  //! open a window to collect the information needed to create a new analysis
+  void createNewAnalysis();
+  //! launch a sub analysis
+  void createSubAnalysis();
+  //\@}
+
+  //! \defgroup MotifHandling Handling of motif lists
+  //\@{
+  //! load motifs
+  void loadMotifList();
+  void saveMotifList();
+  void toggleMotifs();
+  //\@}
+
+  //! load a mussa parameter file (which specifies an analysis to run)
+  void loadMupa();
+  //! load a previously run analysis
+  void loadSavedAnalysis();
+  //! set the soft threshold used by the Nway_Path algorithm
+  void setSoftThreshold(int thres);
+
+  void showMussaToolbar();
+  
+protected:
+  Mussa *analysis;
+  // display our wonderful mussa output
+  SequenceBrowserWidget browser;
+  QToolBar mussaViewTB;
+  QSpinBox zoomBox;
+  ThresholdWidget threshold;
+
+  QAction *aboutAction;
+  QAction *closeAction;
+  QAction *createNewAnalysisAction;
+  QAction *createSubAnalysisAction;
+  QAction *loadMotifListAction;
+  QAction *loadMupaAction;
+  QAction *loadSavedAnalysisAction;
+  QAction *saveMotifListAction;
+  QAction *showMussaViewToolbarAction;
+  QAction *toggleMotifsAction;
+  QAction *whatsThisAction;
+  QAction *saveBrowserPixmapAction;
+
+  //! initialze the actions
+  void setupActions();
+  //! initialize this windows menu object
+  void setupMainMenu();
+  //! stub function to fill in QActions
+  void NotImplementedBox();
+  //! update the SequenceBrowser with our analysis
+  void updateAnalysis();
+  //! update the view of conserved windows
+  void updateLinks();
+};
+
+#endif
diff --git a/qui/PathScene.cpp b/qui/PathScene.cpp
deleted file mode 100644 (file)
index 4a48882..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-#include <QDir>
-#include <QFileDialog>
-#include <QMessageBox>
-#include <QMouseEvent>
-#include <QRubberBand>
-#include <QRect>
-#include <QString>
-#include <iostream>
-#include <set>
-
-#include <GL/gl.h>
-#include <math.h>
-
-#include "qui/PathScene.hpp"
-#include "alg/glsequence.hpp"
-#include "mussa_exceptions.hpp"
-
-using namespace std;
-
-PathScene::PathScene(QWidget *parent)
-  : QGLWidget(parent),
-    rubberBand(0),
-    drawingBand(false)
-{ 
-}
-
-QSize PathScene::sizeHint() const
-{
-  //return QSize((int)GlTracks::viewportHeight(), (int)GlTracks::viewportWidth());
-  return QSize(600, 400);
-}
-
-void PathScene::setViewportCenter(float x)
-{
-  const float epsilon = 1e-10;
-  float center = GlTracks::viewportCenter();
-  float difference = fabsf(x - center);
-  float abs_x = fabsf(x);
-  center = fabsf(center);
-
-  // the difference < epsilon * val is one of the recommended tests
-  // for float equality.
-  // of course since we're looking for not equals, we need to toss a
-  // not at the beginning
-  if (not (difference < epsilon * abs_x or difference < epsilon * center))
-  {
-    GlTracks::setViewportCenter(x);
-    emit viewportChanged();
-    update();
-  }
-}
-
-void PathScene::setZoom(int new_zoom)
-{
-  if (new_zoom != GlTracks::zoom()) {
-    GlTracks::setZoom(new_zoom);
-    emit viewportChanged();
-    update();
-  }
-}
-
-void PathScene::setClipPlane(int )
-{
-/*
-  if (clipZ != (double) newZ){
-    clipZ = (double) newZ;
-    update();
-  }
-*/
-}
-
-void PathScene::clear()
-{
-  GlTracks::clear();
-  emit tracksChanged();
-}
-
-void PathScene::push_sequence(const Sequence &s)
-{
-  GlTracks::push_sequence(s);
-  emit tracksChanged();
-}
-
-void PathScene::push_sequence(GlSequence &gs)
-{
-  GlTracks::push_sequence(gs);
-  emit tracksChanged();
-}
-
-////////////////////
-// Rendering code
-void PathScene::initializeGL()
-{
-  GlTracks::initializeGL();
-}
-
-void PathScene::resizeGL(int width, int height)
-{
-  GlTracks::resizeGL(width, height);
-}
-
-void PathScene::paintGL()
-{
-  GlTracks::paintGL();
-}
-
-void PathScene::mousePressEvent( QMouseEvent *e)
-{
-  drawingBand = true;
-
-  selectedMode = false;
-  bandOrigin = e->pos();
-  if (!rubberBand)
-    rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
-  
-  rubberBand->setGeometry(QRect(bandOrigin, QSize()));
-  rubberBand->show();
-}
-
-void PathScene::mouseMoveEvent( QMouseEvent *e)
-{
-  if (drawingBand)
-    rubberBand->setGeometry(QRect(bandOrigin, e->pos()).normalized());
-}
-
-void PathScene::mouseReleaseEvent( QMouseEvent *e)
-{
-  drawingBand = false;
-  rubberBand->hide();
-  QRect r = QRect(bandOrigin, e->pos()).normalized();
-  bandOrigin = r.topLeft();
-
-  selectRegion(r.top(), r.left(), r.bottom(), r.right());
-}
diff --git a/qui/PathScene.hpp b/qui/PathScene.hpp
deleted file mode 100644 (file)
index ef4a357..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef _PATHSCENE_H_
-#define _PATHSCENE_H_
-
-#include <QGLWidget>
-#include <QRectF>
-#include <QPoint>
-#include <vector>
-#include <GL/gl.h>
-
-#include "alg/mussa.hpp"
-#include "alg/glsequence.hpp"
-#include "alg/gltracks.hpp"
-
-class QMouseEvent;
-class QRubberBand;
-
-/*! \brief Render mussa sequences and paths 
- */
-class PathScene: public QGLWidget, public GlTracks
-{
-  Q_OBJECT
-
-public: 
-  PathScene(QWidget *parent=0);
-
-  QSize sizeHint() const;
-
-  void clear();
-  void push_sequence(const Sequence &s);
-  void push_sequence(GlSequence &);
-      
-public slots:
-  void setClipPlane(int z);
-  //! set the center of the current viewport
-  void setViewportCenter(float x);
-  //! set our magnification level
-  void setZoom(int);
-
-signals:
-  //! emited when someone adds to our list of tracks
-  void tracksChanged();
-  void viewportChanged();
-
-private:
-  void initializeGL();
-  void resizeGL(int height, int width);
-  void paintGL();
-
-  //! \defgroup Selection
-  QRubberBand *rubberBand;
-  QPoint bandOrigin;
-  bool drawingBand;
-  void mousePressEvent(QMouseEvent *);
-  void mouseMoveEvent(QMouseEvent *);
-  void mouseReleaseEvent(QMouseEvent *);
-};
-#endif
diff --git a/qui/PathSidebar.cpp b/qui/PathSidebar.cpp
deleted file mode 100644 (file)
index 50bf9cc..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "qui/PathSidebar.hpp"
-#include "alg/glsequence.hpp"
-
-using namespace std;
-
-PathSidebar::PathSidebar(QWidget* parent)
-  : QWidget(parent)
-{
-  setLayout(&layout);
-}
-
-void PathSidebar::updateSidebar(PathScene& scene)
-{
-  for (vector<SequenceDescription *>::iterator desc_i = descriptions.begin();
-       desc_i != descriptions.end();
-       ++desc_i)
-  {
-    layout.removeWidget(*desc_i);
-  }
-  descriptions.clear();
-  cout << "sidebar " << scene.tracks().size() << endl;
-  for (vector<GlSequence>::const_iterator track_i = scene.tracks().begin();
-       track_i != scene.tracks().end();
-       ++track_i)
-  {
-    cout << "  " << track_i->sequence().length() << endl;
-    SequenceDescription *desc = new SequenceDescription(this);
-    //disc->setName(track_i->sequence().name());
-    desc->setLength(track_i->sequence().length());
-    //desc->setPosition(track_i->sequence().length());
-    descriptions.push_back(desc);
-    layout.addWidget(desc);
-  }
-  setLayout(&layout);
-}
diff --git a/qui/PathSidebar.hpp b/qui/PathSidebar.hpp
deleted file mode 100644 (file)
index 852f1c9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef _PATH_SIDEBAR_H
-#define _PATH_SIDEBAR_H
-
-#include <vector>
-
-#include <QVBoxLayout>
-#include <QWidget>
-#include "qui/PathScene.hpp"
-#include "qui/SequenceDescription.hpp"
-
-//! store a collection of sequence descriptions for the PathWidget
-class PathSidebar : public QWidget
-{
-public:
-  PathSidebar(QWidget *parent = 0);
-
-  void updateSidebar(PathScene& scene);
-  QVBoxLayout layout;
-  std::vector<SequenceDescription *> descriptions;
-};
-#endif
diff --git a/qui/PathWidget.cpp b/qui/PathWidget.cpp
deleted file mode 100644 (file)
index 1b4cd52..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <iostream>
-
-#include <QLabel>
-#include <QScrollBar>
-#include <QSpacerItem>
-#include <QSplitter>
-#include <QVBoxLayout>
-#include <QWidget>
-
-#include "qui/PathScene.hpp"
-#include "qui/PathWidget.hpp"
-#include "qui/ScrollableScene.hpp"
-#include "qui/SequenceDescription.hpp"
-#include "alg/glsequence.hpp"
-
-#include <math.h>
-using namespace std;
-
-PathWidget::PathWidget(QWidget *parent)
-  : QSplitter(parent),
-    scrollable_scene(parent)
-{
-  setOrientation(Qt::Horizontal);
-
-  addWidget(&left_sidebar);
-  addWidget(&scrollable_scene);
-  addWidget(&right_sidebar);
-
-  connect(&scrollable_scene.scene(), SIGNAL(viewportChanged()),
-          this, SLOT(updatePosition()));
-}
-
-void PathWidget::updateTracks()
-{
-  left_sidebar.updateSidebar(scrollable_scene.scene());
-  right_sidebar.updateSidebar(scrollable_scene.scene());
-  updatePosition();
-}
-
-void PathWidget::updatePosition()
-{
-  const PathScene& scene = scrollable_scene.scene();
-  const vector<GlSequence> &tracks = scene.tracks();
-  vector<SequenceDescription *> left = left_sidebar.descriptions;
-  vector<SequenceDescription *> right = right_sidebar.descriptions;
-  for(size_t i = 0; i != tracks.size() and i != right.size(); ++i)
-  {
-    left[i]->setPosition(tracks[i].leftbase(scene.viewportLeft()));
-    right[i]->setPosition(tracks[i].rightbase(scene.viewportRight()));
-  }
-}
diff --git a/qui/PathWidget.hpp b/qui/PathWidget.hpp
deleted file mode 100644 (file)
index 7c47f42..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef _PATH_WIDGET_H_
-#define _PATH_WIDGET_H_
-
-#include <QSplitter>
-
-#include "qui/ScrollableScene.hpp"
-#include "qui/PathSidebar.hpp"
-
-class PathWidget : public QSplitter
-{
-  Q_OBJECT 
-
-public:
-  PathWidget(QWidget *parent=0);
-
-  PathScene& scene() { return scrollable_scene.scene(); }
-  
-public slots:
-  //! when a scene changes its tracks lets update some of our meta info
-  void updateTracks();
-  void updatePosition();
-
-private:
-  ScrollableScene scrollable_scene;
-  PathSidebar left_sidebar;
-  PathSidebar right_sidebar;
-};
-#endif
diff --git a/qui/PathWindow.cpp b/qui/PathWindow.cpp
deleted file mode 100644 (file)
index 883563a..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-#include <QAction>
-#include <QDir>
-#include <QFileDialog>
-#include <QHBoxLayout>
-#include <QIcon>
-#include <QMenuBar>
-#include <QMessageBox>
-#include <QScrollBar>
-#include <QStatusBar>
-#include <QString>
-#include <QWhatsThis>
-
-#include "qui/PathScene.hpp"
-#include "qui/PathWindow.hpp"
-#include "qui/ImageSaveDialog.hpp"
-#include "mussa_exceptions.hpp"
-
-#include <iostream>
-
-using namespace std;
-
-PathWindow::PathWindow(Mussa *analysis_, QWidget *parent) :
-  QMainWindow(parent),
-  analysis(analysis_),
-  path_view(this),
-  mussaViewTB("Path Views"),
-  zoomBox(),
-  threshold(),
-  closeAction(0) // initialize one of the pointers to null as a saftey flag
-{
-  setupActions();
-  setupMainMenu();
-
-  //This next setWhatsThis function prevents
-  // a segfault when using WhatsThis feature with 
-  // opengl widget.
-  //scene->setWhatsThis(tr("Mussa in OpenGL!"));
-  setCentralWidget(&path_view);
-
-  mussaViewTB.addAction(toggleMotifsAction);
-
-  zoomBox.setWhatsThis("zoom magnification factor");
-  zoomBox.setRange(2,1000);
-  mussaViewTB.addWidget(&zoomBox);
-  connect(&zoomBox, SIGNAL(valueChanged(int)), 
-          &path_view.scene(), SLOT(setZoom(int)));
-  
-  threshold.setRange(19, 30);
-  threshold.setThreshold(19);
-  //scene->setClipPlane(20);
-  // FIXME: for when we get the paths drawn at the appropriate depth
-  //connect(&threshold, SIGNAL(thresholdChanged(int)),
-  //        this, SLOT(setClipPlane(int)));
-  connect(&threshold, SIGNAL(thresholdChanged(int)),
-          this, SLOT(setSoftThreshold(int)));
-  mussaViewTB.addWidget(&threshold);
-
-  addToolBar(&mussaViewTB);
-
-  statusBar()->showMessage("Welcome to mussa", 2000);
-  updateAnalysis();
-}
-
-void PathWindow::setupActions()
-{
-  // we really don't want to run this more than once.
-  assert (closeAction == 0);
-
-  // the ever popular about box
-  aboutAction = new QAction(tr("&About"), this);
-  connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
-  aboutAction->setIcon(QIcon("icons/info.png"));
-
-  // add exit
-  closeAction = new QAction(tr("&Close"), this);
-  closeAction->setStatusTip(tr("Close this window"));
-  connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
-  closeAction->setIcon(QIcon("icons/exit.png"));
-  
-  createNewAnalysisAction = new QAction(tr("Define Analysis"), this);
-  connect(createNewAnalysisAction, SIGNAL(triggered()), 
-          this, SLOT(createNewAnalysis()));
-  createNewAnalysisAction->setIcon(QIcon("icons/filenew.png"));
-  
-  createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this);
-  connect(createSubAnalysisAction, SIGNAL(triggered()), 
-          this, SLOT(createSubAnalysis()));
-
-  loadMotifListAction = new QAction(tr("Load Motif List"), this);
-  connect(loadMotifListAction, SIGNAL(triggered()), 
-          this, SLOT(loadMotifList()));
-  
-  loadMupaAction = new QAction(tr("Load Mussa Parameters"), this);
-  connect(loadMupaAction, SIGNAL(triggered()), 
-          this, SLOT(loadMupa()));
-
-  loadSavedAnalysisAction = new QAction(tr("Load &Analysis"), this);
-  connect(loadSavedAnalysisAction, SIGNAL(triggered()), 
-          this, SLOT(loadSavedAnalysis()));
-  loadSavedAnalysisAction->setIcon(QIcon("icons/fileopen.png"));
-
-  saveMotifListAction = new QAction(tr("Save Motifs"), this);
-  connect(saveMotifListAction, SIGNAL(triggered()), 
-          this, SLOT(saveMotifList()));
-  saveMotifListAction->setIcon(QIcon("icons/filesave.png"));
-
-  showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
-  connect(showMussaViewToolbarAction, SIGNAL(triggered()), 
-          this, SLOT(showMussaToolbar()));
-  showMussaViewToolbarAction->setCheckable(true);
-  showMussaViewToolbarAction->setChecked(true);
-
-  toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
-  connect(toggleMotifsAction, SIGNAL(triggered()), 
-          this, SLOT(toggleMotifs()));
-  toggleMotifsAction->setCheckable(true);
-  toggleMotifsAction->setIcon(QIcon("icons/motif_icon.png"));
-  toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
-                                  "You can load motif annotations via "
-                                  "'File->Load Motif List' menu option."));
-
-  whatsThisAction = QWhatsThis::createAction(this);
-  whatsThisAction->setIcon(QIcon("icons/help.png"));
-
-  //Save pixel map action
-  saveOpenGlPixmapAction = new QAction(tr("Save to image..."), this);
-  connect(saveOpenGlPixmapAction, (SIGNAL(triggered())),
-         this, SLOT(promptSaveOpenGlPixmap()));
-  saveOpenGlPixmapAction->setIcon(QIcon("icons/image2.png"));
-}
-
-void PathWindow::setupMainMenu()
-{
-  // we need to run setupActions first
-  assert (closeAction != 0);
-  
-  QMenu *newMenu;
-  newMenu = menuBar()->addMenu(tr("&File"));
-  newMenu->addAction(createNewAnalysisAction);
-  newMenu->addAction(loadMupaAction);
-  newMenu->addAction(loadSavedAnalysisAction);
-  newMenu->addAction(createSubAnalysisAction);
-  newMenu->addSeparator();
-  newMenu->addAction(loadMotifListAction);
-  newMenu->addAction(saveMotifListAction);
-  newMenu->addSeparator();
-  newMenu->addAction(saveOpenGlPixmapAction);
-  newMenu->addSeparator();
-  newMenu->addAction(closeAction);
-
-  newMenu = menuBar()->addMenu(tr("&View"));
-  newMenu->addAction(showMussaViewToolbarAction);
-
-  newMenu = menuBar()->addMenu(tr("&Help"));
-  newMenu->addAction(whatsThisAction);
-  newMenu->addSeparator();
-  newMenu->addAction(aboutAction);
-}
-  
-void PathWindow::about()
-{
-  QMessageBox::about(this, tr("About mussa"),
-      tr("Welcome to Multiple Species Sequence Analysis\n"
-         "(c) 2005-2006 California Institute of Technology\n"
-         "Tristan De Buysscher, Diane Trout\n"));
-}
-
-void PathWindow::createNewAnalysis()
-{
-  NotImplementedBox();
-}
-
-void PathWindow::createSubAnalysis()
-{
-  NotImplementedBox();
-}
-
-
-void PathWindow::loadMotifList()
-{
-  QString caption("Load a motif list");
-  QString filter("Motif list(*.txt *.mtl)");
-  QString path = QFileDialog::getOpenFileName(this,
-                                                   caption, 
-                                                   QDir::currentPath(),
-                                                   filter);
-  // user hit cancel?
-  if (path.isNull()) 
-    return;
-  // try to load safely
-  try {
-    analysis->load_motifs(path.toStdString());
-  } catch (runtime_error e) {
-    QString msg("Unable to load ");
-    msg += path;
-    msg += "\n";
-    msg += e.what();
-    QMessageBox::warning(this, "Load Motifs", msg);
-  }
-  assert (analysis != 0);
-}
-
-void PathWindow::saveMotifList()
-{
-  NotImplementedBox();
-}
-
-void PathWindow::loadMupa()
-{
-  QString caption("Load a mussa parameter file");
-  QString filter("Mussa Parameters (*.mupa)");
-  QString mupa_path = QFileDialog::getOpenFileName(this,
-                                                   caption, 
-                                                   QDir::currentPath(),
-                                                   filter);
-  // user hit cancel?
-  if (mupa_path.isNull()) 
-    return;
-  // try to load safely
-  try {
-    Mussa *m = new Mussa;
-    m->load_mupa_file(mupa_path.toStdString());
-    m->analyze(0, 0, Mussa::TransitiveNway, 0.0);
-    // only switch mussas if we loaded without error
-    delete analysis;
-    analysis = m;
-    updateAnalysis();
-  } catch (mussa_load_error e) {
-    QString msg("Unable to load ");
-    msg += mupa_path;
-    msg += "\n";
-    msg += e.what();
-    QMessageBox::warning(this, "Load Parameter", msg);
-  }
-  assert (analysis != 0);
-}
-
-void PathWindow::loadSavedAnalysis()
-{
-  QString caption("Load a previously run analysis");
-  QString muway_dir = QFileDialog::getExistingDirectory(this,
-                                                        caption, 
-                                                        QDir::currentPath());
-  // user hit cancel?
-  if (muway_dir.isNull()) 
-    return;
-  // try to safely load
-  try {
-    Mussa *m = new Mussa;
-    m->load(muway_dir.toStdString());
-    // only switch mussas if we loaded without error
-    delete analysis;
-    analysis = m;
-    updateAnalysis();
-  } catch (mussa_load_error e) {
-    QString msg("Unable to load ");
-    msg += muway_dir;
-    msg += "\n";
-    msg += e.what();
-    QMessageBox::warning(this, "Load Parameter", msg);
-  }
-  assert (analysis != 0);
-}
-
-void PathWindow::setSoftThreshold(int threshold)
-{
-  if (analysis->get_soft_thres() != threshold) {
-    analysis->set_soft_thres(threshold);
-    analysis->nway();
-    updateLinks();
-    update();
-  }
-}
-
-void PathWindow::showMussaToolbar()
-{
-  if (mussaViewTB.isVisible())
-    mussaViewTB.hide();
-  else
-    mussaViewTB.show();
-}
-
-void PathWindow::toggleMotifs()
-{
-  NotImplementedBox();
-}
-
-void PathWindow::NotImplementedBox()
-{
-  QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
-}      
-
-void PathWindow::promptSaveOpenGlPixmap()
-{
-  QSize size;
-  size = path_view.scene().size();
-  //Image Save Dialog
-  ImageSaveDialog imageSaveDialog(&path_view.scene(), this);
-  imageSaveDialog.setSize(size.width(), size.height());
-  int result = imageSaveDialog.exec();
-  cout << "Result: " << result << "\n";
-}
-
-void PathWindow::updateAnalysis()
-{
-  cout << "analysis updated" << endl;
-  path_view.scene().clear();
-  path_view.scene().setColorMapper(analysis->colorMapper());
-  const vector<Sequence>& seqs = analysis->sequences();
-  for(vector<Sequence>::const_iterator seq_i = seqs.begin();
-      seq_i != seqs.end();
-      ++seq_i)
-  {
-    GlSequence *gs= new GlSequence(*seq_i, analysis->colorMapper());
-    path_view.scene().push_sequence(*gs);
-  }
-  updateLinks();
-  path_view.updateTracks();
-}
-
-void PathWindow::updateLinks()
-{
-  path_view.scene().clear_links();
-  bool reversed = false;
-  const NwayPaths& nway = analysis->paths();
-
-  typedef list<ExtendedConservedPath> conserved_paths;
-  typedef conserved_paths::const_iterator const_conserved_paths_itor;
-  for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
-      path_itor != nway.refined_pathz.end();
-      ++path_itor)
-  {
-    // since we were drawing to the start of a window, and opengl lines
-    // are centered around the two connecting points our lines were slightly
-    // offset. the idea of window_offset is to adjust them to the
-    // right for forward compliment or left for reverse compliment
-    // FIXME: figure out how to unit test these computations
-    //GLfloat window_offset = (path_itor->window_size)/2.0;
-
-    size_t track_len = path_itor->track_indexes.size();
-    vector<int> normalized_path;
-    normalized_path.reserve(track_len);
-    vector<bool> rc_flags(false, track_len);
-    for (size_t track_i=0; track_i != track_len; ++track_i)
-    {
-      int x = path_itor->track_indexes[track_i];
-      // at some point when we modify the pathz data structure to keep
-      // track of the score we can put grab the depth here.
-      //
-      // are we reverse complimented?
-      if ( x>=0) {
-        reversed = false;
-      } else {
-        reversed = true;
-        x = -x; // make positive
-      }
-      normalized_path.push_back(x);
-      rc_flags.push_back(reversed);
-    }
-    path_view.scene().link(normalized_path, rc_flags, path_itor->window_size);
-  }
-  path_view.scene().update();
-}
-
diff --git a/qui/PathWindow.hpp b/qui/PathWindow.hpp
deleted file mode 100644 (file)
index 65bafdf..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#ifndef _PATHWINDOW_H_
-#define _PATHWINDOW_H_
-
-
-
-#include <QMainWindow>
-#include <QPixmap>
-#include <QSpinBox>
-#include <QToolBar>
-
-#include "qui/PathWidget.hpp"
-#include "qui/ThresholdWidget.hpp"
-
-class QAction;
-class Mussa;
-
-class PathWindow : public QMainWindow
-{
-  Q_OBJECT
-
-public: 
-  PathWindow(Mussa* analysis=0, QWidget *parent=0);
-
-public slots:
-  //! display an about box, contemplating the politics of the author list
-  void about();
-
-  //! \defgroup AnalysisFunctions Components related to running analyses
-  //\@{
-  //! open a window to collect the information needed to create a new analysis
-  void createNewAnalysis();
-  //! launch a sub analysis
-  void createSubAnalysis();
-  //\@}
-
-  //! \defgroup MotifHandling Handling of motif lists
-  //\@{
-  //! load motifs
-  void loadMotifList();
-  void saveMotifList();
-  void toggleMotifs();
-  //\@}
-
-  //! load a mussa parameter file (which specifies an analysis to run)
-  void loadMupa();
-  //! load a previously run analysis
-  void loadSavedAnalysis();
-  //! set the soft threshold used by the Nway_Path algorithm
-  void setSoftThreshold(int thres);
-
-  void showMussaToolbar();
-  
-  void promptSaveOpenGlPixmap();
-
-protected:
-  Mussa *analysis;
-  // display our wonderful mussa output
-  PathWidget path_view;
-  QToolBar mussaViewTB;
-  QSpinBox zoomBox;
-  ThresholdWidget threshold;
-
-  QAction *aboutAction;
-  QAction *closeAction;
-  QAction *createNewAnalysisAction;
-  QAction *createSubAnalysisAction;
-  QAction *loadMotifListAction;
-  QAction *loadMupaAction;
-  QAction *loadSavedAnalysisAction;
-  QAction *saveMotifListAction;
-  QAction *showMussaViewToolbarAction;
-  QAction *toggleMotifsAction;
-  QAction *whatsThisAction;
-  QAction *saveOpenGlPixmapAction;
-
-  //! initialze the actions
-  void setupActions();
-  //! initialize this windows menu object
-  void setupMainMenu();
-  //! stub function to fill in QActions
-  void NotImplementedBox();
-  //! update the PathScene with our analysis
-  void updateAnalysis();
-  //! update the view of conserved windows
-  void updateLinks();
-};
-
-#endif
diff --git a/qui/ScrollableScene.cpp b/qui/ScrollableScene.cpp
deleted file mode 100644 (file)
index 0c3de45..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-
-#include <QVBoxLayout>
-
-#include "qui/ScrollableScene.hpp"
-
-ScrollableScene::ScrollableScene(QWidget *parent) :
-  QWidget(parent),
-  viewportBar(Qt::Horizontal)
-{
-  // construct central opengl widget
-  QVBoxLayout *layout = new QVBoxLayout;
-
-  layout->addWidget(&path_scene);
-  layout->addWidget(&viewportBar);
-
-  connect(&viewportBar, SIGNAL(valueChanged(int)), 
-          this, SLOT(setViewportCenter(int)));
-  connect(&path_scene, SIGNAL(viewportChanged()), 
-          this, SLOT(updateScrollBar()));
-
-  setLayout(layout);
-
-  // sets range & scale
-  updateScrollBar();
-}
-
-void ScrollableScene::updateScrollBar()
-{
-  float max_right = path_scene.right();
-  float max_left = path_scene.left();
-  float max_range = max_right - max_left;
-  float cur_left = path_scene.viewportLeft();
-  float cur_right = path_scene.viewportRight();
-  float cur_center = ((cur_right-cur_left)/2)+cur_left;
-  // set range to min
-  thumb = (int)cur_center;
-  viewportBar.setRange((int)max_left, (int)max_right);
-  viewportBar.setValue(thumb);
-}
-
-void ScrollableScene::setViewportCenter(int x)
-{
-  if (x != thumb) {
-    thumb = x;
-    path_scene.setViewportCenter(thumb);
-  }
-}
-
diff --git a/qui/ScrollableScene.hpp b/qui/ScrollableScene.hpp
deleted file mode 100644 (file)
index 4b90e53..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef _SCROLLABLE_SCENE_H_
-#define _SCROLLABLE_SCENE_H_
-
-#include <QScrollBar>
-#include <QWidget>
-#include "qui/PathScene.hpp"
-
-class ScrollableScene : public QWidget
-{
-  Q_OBJECT 
-
-public:
-  ScrollableScene(QWidget *parent=0);
-  PathScene path_scene;
-
-  PathScene& scene() { return path_scene; }
-public slots:
-  //! update the scrollbar with current viewport information
-  void updateScrollBar();
-  //! update scene with the properly scalled scrollbar offset
-  void setViewportCenter(int x);
-
-private:
-  QScrollBar viewportBar;
-  int thumb;
-
-  float range;
-  float scale;
-};
-
-#endif
diff --git a/qui/SequenceDescription.cpp b/qui/SequenceDescription.cpp
deleted file mode 100644 (file)
index 130483b..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "qui/SequenceDescription.hpp"
-
-#include <QVBoxLayout>
-
-using namespace std;
-
-SequenceDescription::SequenceDescription(QWidget *parent)
-  : QWidget(parent)
-{
-  createWidget();
-}
-
-SequenceDescription::SequenceDescription(string& name, float length, 
-                                         QWidget *parent)
-  : QWidget(parent)
-{
-  setName(name);
-  setLength(length);
-  createWidget();
-}
-
-void SequenceDescription::createWidget()
-{
-  QLayout *layout = new QVBoxLayout;
-  layout->setSpacing(2);
-  layout->addWidget(&name_label);
-  layout->addWidget(&length_label);
-  layout->addWidget(&position_label);
-  setLayout(layout);
-}
-
-void SequenceDescription::setLength(float length)
-{
-  QString s;
-  if (length > 1000) {
-    length /= 1000;
-    s.setNum(length, 'f', 2);
-    s += "kb";
-  } else {
-    s.setNum(length);
-    s += "b";
-  }
-  length_label.setText(s);
-}
-
diff --git a/qui/SequenceDescription.hpp b/qui/SequenceDescription.hpp
deleted file mode 100644 (file)
index 88e01ef..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef _SEQUENCE_DESCRIPTION_H
-#define _SEQUENCE_DESCRIPTION_H
-
-#include <string>
-
-#include <QString>
-#include <QLabel>
-#include <QWidget>
-
-class SequenceDescription : public QWidget
-{
-  Q_OBJECT 
-
-public:
-  SequenceDescription(QWidget *parent=0);
-  SequenceDescription(std::string& name, float length, QWidget *parent=0);
-
-  void setName(std::string& name) { name_label.setText(name.c_str()); }
-  void setLength(float length);
-  void setPosition(int pos) { QString s; position_label.setText(s.setNum(pos));}
-
-private:
-  QLabel name_label;
-  QLabel length_label;
-  QLabel position_label;
-
-  void createWidget();
-};
-#endif
diff --git a/qui/seqbrowser/ScrollableSequenceBrowser.cpp b/qui/seqbrowser/ScrollableSequenceBrowser.cpp
new file mode 100644 (file)
index 0000000..0349c87
--- /dev/null
@@ -0,0 +1,47 @@
+#include <QVBoxLayout>
+
+#include "qui/seqbrowser/ScrollableSequenceBrowser.hpp"
+
+ScrollableSequenceBrowser::ScrollableSequenceBrowser(QWidget *parent) :
+  QWidget(parent),
+  viewportBar(Qt::Horizontal)
+{
+  // construct central opengl widget
+  QVBoxLayout *layout = new QVBoxLayout;
+
+  layout->addWidget(&sequence_browser);
+  layout->addWidget(&viewportBar);
+
+  connect(&viewportBar, SIGNAL(valueChanged(int)), 
+          this, SLOT(setViewportCenter(int)));
+  connect(&sequence_browser, SIGNAL(viewportChanged()), 
+          this, SLOT(updateScrollBar()));
+
+  setLayout(layout);
+
+  // sets range & scale
+  updateScrollBar();
+}
+
+void ScrollableSequenceBrowser::updateScrollBar()
+{
+  float max_right = sequence_browser.right();
+  float max_left = sequence_browser.left();
+  float max_range = max_right - max_left;
+  float cur_left = sequence_browser.viewportLeft();
+  float cur_right = sequence_browser.viewportRight();
+  float cur_center = ((cur_right-cur_left)/2)+cur_left;
+  // set range to min
+  thumb = (int)cur_center;
+  viewportBar.setRange((int)max_left, (int)max_right);
+  viewportBar.setValue(thumb);
+}
+
+void ScrollableSequenceBrowser::setViewportCenter(int x)
+{
+  if (x != thumb) {
+    thumb = x;
+    sequence_browser.setViewportCenter(thumb);
+  }
+}
+
diff --git a/qui/seqbrowser/ScrollableSequenceBrowser.hpp b/qui/seqbrowser/ScrollableSequenceBrowser.hpp
new file mode 100644 (file)
index 0000000..b8a470e
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _SCROLLABLE_SEQUENCE_BROWSER_H_
+#define _SCROLLABLE_SEQUENCE_BROWSER_H_
+
+#include <QScrollBar>
+#include <QWidget>
+#include "qui/seqbrowser/SequenceBrowser.hpp"
+
+class ScrollableSequenceBrowser : public QWidget
+{
+  Q_OBJECT 
+
+public:
+  ScrollableSequenceBrowser(QWidget *parent=0);
+  SequenceBrowser sequence_browser;
+
+  SequenceBrowser& browser() { return sequence_browser; }
+  const SequenceBrowser& browser() const { return sequence_browser; }
+public slots:
+  //! update the scrollbar with current viewport information
+  void updateScrollBar();
+  //! update scene with the properly scalled scrollbar offset
+  void setViewportCenter(int x);
+
+private:
+  QScrollBar viewportBar;
+  int thumb;
+
+  float range;
+  float scale;
+};
+
+#endif
diff --git a/qui/seqbrowser/SequenceBrowser.cpp b/qui/seqbrowser/SequenceBrowser.cpp
new file mode 100644 (file)
index 0000000..1ff2339
--- /dev/null
@@ -0,0 +1,133 @@
+#include <QDir>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QMouseEvent>
+#include <QRubberBand>
+#include <QRect>
+#include <QString>
+#include <iostream>
+#include <set>
+
+#include <GL/gl.h>
+#include <math.h>
+
+#include "qui/seqbrowser/SequenceBrowser.hpp"
+#include "mussa_exceptions.hpp"
+
+using namespace std;
+
+SequenceBrowser::SequenceBrowser(QWidget *parent)
+  : QGLWidget(parent),
+    rubberBand(0),
+    drawingBand(false)
+{ 
+}
+
+QSize SequenceBrowser::sizeHint() const
+{
+  //return QSize((int)GlSeqBrowser::viewportHeight(), (int)GlSeqBrowser::viewportWidth());
+  return QSize(600, 400);
+}
+
+void SequenceBrowser::setViewportCenter(float x)
+{
+  const float epsilon = 1e-10;
+  float center = GlSeqBrowser::viewportCenter();
+  float difference = fabsf(x - center);
+  float abs_x = fabsf(x);
+  center = fabsf(center);
+
+  // the difference < epsilon * val is one of the recommended tests
+  // for float equality.
+  // of course since we're looking for not equals, we need to toss a
+  // not at the beginning
+  if (not (difference < epsilon * abs_x or difference < epsilon * center))
+  {
+    GlSeqBrowser::setViewportCenter(x);
+    emit viewportChanged();
+    update();
+  }
+}
+
+void SequenceBrowser::setZoom(int new_zoom)
+{
+  if (new_zoom != GlSeqBrowser::zoom()) {
+    GlSeqBrowser::setZoom(new_zoom);
+    emit viewportChanged();
+    update();
+  }
+}
+
+void SequenceBrowser::setClipPlane(int )
+{
+/*
+  if (clipZ != (double) newZ){
+    clipZ = (double) newZ;
+    update();
+  }
+*/
+}
+
+void SequenceBrowser::clear()
+{
+  GlSeqBrowser::clear();
+  emit tracksChanged();
+}
+
+void SequenceBrowser::push_sequence(const Sequence &s)
+{
+  GlSeqBrowser::push_sequence(s);
+  emit tracksChanged();
+}
+
+void SequenceBrowser::push_sequence(GlSequence &gs)
+{
+  GlSeqBrowser::push_sequence(gs);
+  emit tracksChanged();
+}
+
+////////////////////
+// Rendering code
+void SequenceBrowser::initializeGL()
+{
+  GlSeqBrowser::initializeGL();
+}
+
+void SequenceBrowser::resizeGL(int width, int height)
+{
+  GlSeqBrowser::resizeGL(width, height);
+}
+
+void SequenceBrowser::paintGL()
+{
+  GlSeqBrowser::paintGL();
+}
+
+void SequenceBrowser::mousePressEvent( QMouseEvent *e)
+{
+  drawingBand = true;
+
+  selectedMode = false;
+  bandOrigin = e->pos();
+  if (!rubberBand)
+    rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
+  
+  rubberBand->setGeometry(QRect(bandOrigin, QSize()));
+  rubberBand->show();
+}
+
+void SequenceBrowser::mouseMoveEvent( QMouseEvent *e)
+{
+  if (drawingBand)
+    rubberBand->setGeometry(QRect(bandOrigin, e->pos()).normalized());
+}
+
+void SequenceBrowser::mouseReleaseEvent( QMouseEvent *e)
+{
+  drawingBand = false;
+  rubberBand->hide();
+  QRect r = QRect(bandOrigin, e->pos()).normalized();
+  bandOrigin = r.topLeft();
+
+  selectRegion(r.top(), r.left(), r.bottom(), r.right());
+}
diff --git a/qui/seqbrowser/SequenceBrowser.hpp b/qui/seqbrowser/SequenceBrowser.hpp
new file mode 100644 (file)
index 0000000..ed3da99
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _PATHSCENE_H_
+#define _PATHSCENE_H_
+
+#include <QGLWidget>
+#include <QRectF>
+#include <QPoint>
+#include <vector>
+#include <GL/gl.h>
+
+#include "alg/mussa.hpp"
+#include "alg/glsequence.hpp"
+#include "alg/glseqbrowser.hpp"
+
+class QMouseEvent;
+class QRubberBand;
+
+/*! \brief Render mussa sequences and paths 
+ */
+class SequenceBrowser: public QGLWidget, public GlSeqBrowser
+{
+  Q_OBJECT
+
+public: 
+  SequenceBrowser(QWidget *parent=0);
+
+  QSize sizeHint() const;
+
+  void clear();
+  void push_sequence(const Sequence &s);
+  void push_sequence(GlSequence &);
+      
+public slots:
+  void setClipPlane(int z);
+  //! set the center of the current viewport
+  void setViewportCenter(float x);
+  //! set our magnification level
+  void setZoom(int);
+
+signals:
+  //! emited when someone adds to our list of tracks
+  void tracksChanged();
+  void viewportChanged();
+
+private:
+  void initializeGL();
+  void resizeGL(int height, int width);
+  void paintGL();
+
+  //! \defgroup Selection
+  QRubberBand *rubberBand;
+  QPoint bandOrigin;
+  bool drawingBand;
+  void mousePressEvent(QMouseEvent *);
+  void mouseMoveEvent(QMouseEvent *);
+  void mouseReleaseEvent(QMouseEvent *);
+};
+#endif
diff --git a/qui/seqbrowser/SequenceBrowserSidebar.cpp b/qui/seqbrowser/SequenceBrowserSidebar.cpp
new file mode 100644 (file)
index 0000000..b09a858
--- /dev/null
@@ -0,0 +1,37 @@
+#include "qui/seqbrowser/SequenceBrowserSidebar.hpp"
+
+using namespace std;
+
+SequenceBrowserSidebar::SequenceBrowserSidebar(QWidget* parent)
+  : QWidget(parent)
+{
+  setLayout(&layout);
+}
+
+void SequenceBrowserSidebar::clear()
+{
+  for (vector<SequenceDescription *>::iterator desc_i = descriptions.begin();
+       desc_i != descriptions.end();
+       ++desc_i)
+  {
+    layout.removeWidget(*desc_i);
+  }
+  descriptions.clear();
+}
+
+void SequenceBrowserSidebar::setSequences(vector<GlSequence>& sequences)
+{
+  clear();
+  for (vector<GlSequence>::const_iterator track_i = sequences.begin();
+       track_i != sequences.end();
+       ++track_i)
+  {
+    SequenceDescription *desc = new SequenceDescription(this);
+    //disc->setName(track_i->sequence().name());
+    desc->setLength(track_i->sequence().length());
+    //desc->setPosition(track_i->sequence().length());
+    descriptions.push_back(desc);
+    layout.addWidget(desc);
+  }
+  setLayout(&layout);
+}
diff --git a/qui/seqbrowser/SequenceBrowserSidebar.hpp b/qui/seqbrowser/SequenceBrowserSidebar.hpp
new file mode 100644 (file)
index 0000000..48bc78e
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _SEQUENCE_BROWSER_SIDEBAR_H
+#define _SEQUENCE_BROWSER_SIDEBAR_H
+
+#include <vector>
+
+#include <QVBoxLayout>
+#include <QWidget>
+#include "qui/seqbrowser/SequenceBrowser.hpp"
+#include "qui/seqbrowser/SequenceDescription.hpp"
+#include "alg/glsequence.hpp"
+
+//! store a collection of sequence descriptions for the SequenceBrowserWidget
+class SequenceBrowserSidebar : public QWidget
+{
+  Q_OBJECT
+
+public:
+  enum SidebarSide { Left, Right };
+
+  SequenceBrowserSidebar(QWidget *parent = 0);
+
+  //! clear our list of descriptions
+  void clear();
+  void setSequences(std::vector<GlSequence>& );
+
+  std::vector<SequenceDescription *> descriptions;
+public slots:
+  //! update the reported sequence position for each of our sequences
+  /*! for example when the right side is at position 3000 call 
+   *  iterate over each sequence and get its rightbase pair value
+   */
+  //void updatePositionValues(float left, float right);
+
+private:
+  QVBoxLayout layout;
+  //! for updating positions we need to know which side we're on
+  //SidebarSide sidebar_side;
+};
+#endif
diff --git a/qui/seqbrowser/SequenceBrowserWidget.cpp b/qui/seqbrowser/SequenceBrowserWidget.cpp
new file mode 100644 (file)
index 0000000..215bcd3
--- /dev/null
@@ -0,0 +1,130 @@
+#include <iostream>
+
+#include <QLabel>
+#include <QScrollBar>
+#include <QSpacerItem>
+#include <QSplitter>
+#include <QVBoxLayout>
+#include <QWidget>
+
+#include "qui/seqbrowser/SequenceBrowserWidget.hpp"
+#include "qui/seqbrowser/SequenceBrowser.hpp"
+#include "qui/seqbrowser/ScrollableSequenceBrowser.hpp"
+#include "qui/seqbrowser/SequenceDescription.hpp"
+#include "qui/ImageSaveDialog.hpp"
+
+#include "alg/glsequence.hpp"
+
+#include <math.h>
+using namespace std;
+
+SequenceBrowserWidget::SequenceBrowserWidget(QWidget *parent)
+  : QSplitter(parent),
+    scrollable_browser(parent)
+{
+  setOrientation(Qt::Horizontal);
+
+  addWidget(&left_sidebar);
+  addWidget(&scrollable_browser);
+  addWidget(&right_sidebar);
+
+  connect(&scrollable_browser.browser(), SIGNAL(viewportChanged()),
+          this, SLOT(updatePosition()));
+}
+
+void SequenceBrowserWidget::clear()
+{
+  converted_sequences.clear();
+  scrollable_browser.browser().clear();
+  left_sidebar.clear();
+  right_sidebar.clear();
+}
+
+void SequenceBrowserWidget::setSequences(const std::vector<Sequence>& sequences,
+                                            AnnotationColors& cm)
+{
+  SequenceBrowser& browser = scrollable_browser.browser();
+  clear();
+  for(vector<Sequence>::const_iterator seq_i = sequences.begin();
+      seq_i != sequences.end();
+      ++seq_i)
+  {
+    GlSequence *gs= new GlSequence(*seq_i, cm);
+    converted_sequences.push_back(*gs);
+    browser.push_sequence(*gs);
+  }
+  left_sidebar.setSequences(converted_sequences);
+  right_sidebar.setSequences(converted_sequences);
+  updatePosition();
+}
+
+void SequenceBrowserWidget::setSequences(std::vector<GlSequence>& sequences)
+{
+  SequenceBrowser& browser = scrollable_browser.browser();
+  clear();
+  for(vector<GlSequence>::iterator seq_i = sequences.begin();
+      seq_i != sequences.end();
+      ++seq_i)
+  {
+    browser.push_sequence(*seq_i);
+  }
+  left_sidebar.setSequences(sequences);
+  right_sidebar.setSequences(sequences);
+  updatePosition();
+}
+
+const vector<GlSequence>& SequenceBrowserWidget::sequences() const
+{
+  return scrollable_browser.browser().sequences();
+}
+
+void SequenceBrowserWidget::clear_links()
+{
+  scrollable_browser.browser().clear_links();
+}
+
+void SequenceBrowserWidget::link(const std::vector<int>& path, 
+                                 const std::vector<bool>& isRC, 
+                                 int length)
+{
+  scrollable_browser.browser().link(path, isRC, length);
+}
+
+/* This could theoretically be pushed down to some set 
+ * of signals and slots connecting SequenceDescriptions and 
+ * some signal emitted by the browser's viewportChanged code
+ * but evertime I tried to figure it out, I got confused about 
+ * how the descriptions in one of the sidebars was supposed to know
+ * if it was mapping the rightbase or the leftbase.
+ * And so though this could be better the typical use cases 
+ * can just talk to the SequenceBrowserWidget for rendering
+ * or mussa output
+ */
+void SequenceBrowserWidget::updatePosition()
+{
+  const SequenceBrowser& browser = scrollable_browser.browser();
+  const vector<GlSequence> &sequences = browser.sequences();
+  vector<SequenceDescription *> left = left_sidebar.descriptions;
+  vector<SequenceDescription *> right = right_sidebar.descriptions;
+  for(size_t i = 0; i != sequences.size() and i != right.size(); ++i)
+  {
+    left[i]->setPosition(sequences[i].leftbase(browser.viewportLeft()));
+    right[i]->setPosition(sequences[i].rightbase(browser.viewportRight()));
+  }
+}
+
+void SequenceBrowserWidget::promptSaveBrowserPixmap()
+{
+  QSize size;
+  size = scrollable_browser.browser().size();
+  //Image Save Dialog
+  ImageSaveDialog imageSaveDialog(&scrollable_browser.browser(), this);
+  imageSaveDialog.setSize(size.width(), size.height());
+  int result = imageSaveDialog.exec();
+  cout << "Result: " << result << "\n";
+}
+
+void SequenceBrowserWidget::setZoom(int z)
+{
+  scrollable_browser.browser().setZoom(z);
+}
diff --git a/qui/seqbrowser/SequenceBrowserWidget.hpp b/qui/seqbrowser/SequenceBrowserWidget.hpp
new file mode 100644 (file)
index 0000000..e51f445
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef _PATH_WIDGET_H_
+#define _PATH_WIDGET_H_
+
+#include <vector>
+
+#include <QSplitter>
+
+#include "alg/sequence.hpp"
+#include "alg/glsequence.hpp"
+#include "qui/seqbrowser/ScrollableSequenceBrowser.hpp"
+#include "qui/seqbrowser/SequenceBrowserSidebar.hpp"
+
+//! the master widget that makes a useful Sequence Browser
+class SequenceBrowserWidget : public QSplitter
+{
+  Q_OBJECT 
+
+public:
+  SequenceBrowserWidget(QWidget *parent=0);
+
+  //SequenceBrowser& browser() { return scrollable_browser.browser(); }
+  //! reset all of our stored data back to the empty state
+  void clear();
+
+  /* Attach sequences to our browser, its best to attach them all
+   * at once, as otherwise the Sidebar has some rendering trouble
+   * as it draw and redraws the SequenceDescriptions in the various
+   * positions.
+   */
+  //void push_sequences(std::vector<Sequence>& sequences);
+  void setSequences(const std::vector<Sequence>& sequences, AnnotationColors& cm);
+  void setSequences(std::vector<GlSequence>& sequences);
+  const std::vector<GlSequence>& sequences() const;
+
+  //! reset just the links we're displaying
+  void clear_links();
+  //! set per species links
+  void link(const std::vector<int> &path, const std::vector<bool>& isRC, int length);
+  
+public slots:
+  //! set the zoom level of our browser
+  void setZoom(int);
+
+  void updatePosition();
+  //! ask the user where to save an image of the current browser view
+  void promptSaveBrowserPixmap();
+
+private:
+  ScrollableSequenceBrowser scrollable_browser;
+  SequenceBrowserSidebar left_sidebar;
+  SequenceBrowserSidebar right_sidebar;
+
+  //! sequences created by a setSequences(vector<Sequences>) call
+  /*! I need to save them so i can free them to avoid a memory leak 
+   */
+  std::vector<GlSequence> converted_sequences;
+};
+#endif
diff --git a/qui/seqbrowser/SequenceDescription.cpp b/qui/seqbrowser/SequenceDescription.cpp
new file mode 100644 (file)
index 0000000..f62d52b
--- /dev/null
@@ -0,0 +1,45 @@
+#include <QVBoxLayout>
+
+#include "qui/seqbrowser/SequenceDescription.hpp"
+
+using namespace std;
+
+SequenceDescription::SequenceDescription(QWidget *parent)
+  : QWidget(parent)
+{
+  createWidget();
+}
+
+SequenceDescription::SequenceDescription(string& name, float length, 
+                                         QWidget *parent)
+  : QWidget(parent)
+{
+  setName(name);
+  setLength(length);
+  createWidget();
+}
+
+void SequenceDescription::createWidget()
+{
+  QLayout *layout = new QVBoxLayout;
+  layout->setSpacing(2);
+  layout->addWidget(&name_label);
+  layout->addWidget(&length_label);
+  layout->addWidget(&position_label);
+  setLayout(layout);
+}
+
+void SequenceDescription::setLength(float length)
+{
+  QString s;
+  if (length > 1000) {
+    length /= 1000;
+    s.setNum(length, 'f', 2);
+    s += "kb";
+  } else {
+    s.setNum(length);
+    s += "b";
+  }
+  length_label.setText(s);
+}
+
diff --git a/qui/seqbrowser/SequenceDescription.hpp b/qui/seqbrowser/SequenceDescription.hpp
new file mode 100644 (file)
index 0000000..88e01ef
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _SEQUENCE_DESCRIPTION_H
+#define _SEQUENCE_DESCRIPTION_H
+
+#include <string>
+
+#include <QString>
+#include <QLabel>
+#include <QWidget>
+
+class SequenceDescription : public QWidget
+{
+  Q_OBJECT 
+
+public:
+  SequenceDescription(QWidget *parent=0);
+  SequenceDescription(std::string& name, float length, QWidget *parent=0);
+
+  void setName(std::string& name) { name_label.setText(name.c_str()); }
+  void setLength(float length);
+  void setPosition(int pos) { QString s; position_label.setText(s.setNum(pos));}
+
+private:
+  QLabel name_label;
+  QLabel length_label;
+  QLabel position_label;
+
+  void createWidget();
+};
+#endif