3fe84d58eb28620c419425e10509d595216bb9b1
[mussa.git] / qui / seqbrowser / SequenceBrowser.cpp
1 #include <QApplication>
2 #include <QClipboard>
3 #include <QDir>
4 #include <QFileDialog>
5 #include <QMessageBox>
6 #include <QMouseEvent>
7 #include <QWheelEvent>
8 #include <QRubberBand>
9 #include <QRect>
10 #include <QString>
11 #include <iostream>
12 #include <set>
13
14 #include <math.h>
15
16 #include "qui/seqbrowser/SequenceBrowser.hpp"
17 #include "mussa_exceptions.hpp"
18
19 using namespace std;
20
21 SequenceBrowser::SequenceBrowser(QWidget *parent)
22   : QGLWidget(parent),
23     rubberBand(0),
24     popupMenu(0),
25     copySelectedSequenceAsFastaAction(0),
26     copySelectedSequenceAsStringAction(0),
27     editSequencePropertiesAction(0),
28     clearSelectionAction(0)
29
30   popupMenu = new QMenu(this);
31   copySelectedSequenceAsFastaAction = new QAction(tr("&Copy as Fasta"), this);
32   connect(copySelectedSequenceAsFastaAction, SIGNAL(triggered()), 
33           this, SLOT(copySelectedSequenceAsFasta()));
34   popupMenu->addAction(copySelectedSequenceAsFastaAction);
35
36   copySelectedSequenceAsStringAction = new QAction(tr("&Copy Sequence"), this);
37   copySelectedSequenceAsStringAction->setShortcut(Qt::CTRL | Qt::Key_C);
38   connect(copySelectedSequenceAsStringAction, SIGNAL(triggered()), 
39           this, SLOT(copySelectedSequenceAsString()));
40   popupMenu->addAction(copySelectedSequenceAsStringAction);
41
42   // connect edit properties action
43   editSequencePropertiesAction = new QAction(tr("Sequence &Properties"), this);
44   connect(editSequencePropertiesAction, SIGNAL(triggered()), 
45           this, SLOT(editSequenceProperties()));
46           
47   clearSelectionAction = new QAction(tr("Clear Selection"), this);
48   clearSelectionAction->setShortcut(Qt::Key_Escape);
49   connect(clearSelectionAction, SIGNAL(triggered()), 
50           this, SLOT(clearSelection()));
51   this->addAction(clearSelectionAction);          
52   
53 }
54
55 SequenceBrowser::SequenceBrowser(const SequenceBrowser& sb, QWidget *parent)
56   : QGLWidget(parent),
57     GlSeqBrowser(sb),
58     rubberBand(sb.rubberBand),
59     popupMenu(sb.popupMenu),
60     copySelectedSequenceAsFastaAction(sb.copySelectedSequenceAsFastaAction),
61     copySelectedSequenceAsStringAction(sb.copySelectedSequenceAsStringAction),
62     editSequencePropertiesAction(sb.editSequencePropertiesAction),
63     clearSelectionAction(sb.clearSelectionAction)
64 {
65   resize(sb.width(), sb.height());
66   setZoom(sb.zoom());
67   paintGL();
68 }
69
70 QMenu *SequenceBrowser::getPopupMenu()
71 {
72   return popupMenu;
73 }
74
75 QAction *SequenceBrowser::getCopySelectedSequenceAsFastaAction()
76 {
77   return copySelectedSequenceAsFastaAction;
78 }
79
80 QAction *SequenceBrowser::getCopySelectedSequenceAsStringAction()
81 {
82   return copySelectedSequenceAsStringAction;
83 }
84
85 QAction *SequenceBrowser::getEditSequencePropertiesAction()
86 {
87   return editSequencePropertiesAction;
88 }
89
90 QSize SequenceBrowser::sizeHint() const
91 {
92   return QSize(viewportPixelWidth(), viewportPixelHeight());
93 }
94
95 void SequenceBrowser::setViewportCenter(float x)
96 {
97   const float epsilon = 1e-10;
98   float center = GlSeqBrowser::viewportCenter();
99   float difference = fabsf(x - center);
100   float abs_x = fabsf(x);
101   center = fabsf(center);
102
103   // the difference < epsilon * val is one of the recommended tests
104   // for float equality.
105   // of course since we're looking for not equals, we need to toss a
106   // not at the beginning
107   if (not (difference < epsilon * abs_x or difference < epsilon * center))
108   {
109     GlSeqBrowser::setViewportCenter(x);
110     emit viewportChanged();
111     update();
112   }
113 }
114
115 void SequenceBrowser::setZoom(double new_zoom)
116 {
117   if (new_zoom != GlSeqBrowser::zoom()) {
118     GlSeqBrowser::setZoom(new_zoom);
119     emit viewportChanged();
120     update();
121   }
122 }
123
124 void SequenceBrowser::setClipPlane(int )
125 {
126 /*
127   if (clipZ != (double) newZ){
128     clipZ = (double) newZ;
129     update();
130   }
131 */
132 }
133
134 void SequenceBrowser::copySelectedSequenceAsFasta()
135 {
136   // get fasta data
137   std::string buffer;
138   size_t base_pairs_copied = copySelectedTracksAsFasta(buffer);
139
140   // get reference to clipboard
141   QClipboard *clipboard = QApplication::clipboard();
142   clipboard->setText(buffer.c_str());
143   emit basepairsCopied(base_pairs_copied);
144 }
145
146 void SequenceBrowser::copySelectedSequenceAsString()
147 {
148   // get fasta data
149   std::string buffer;
150   size_t base_pairs_copied = copySelectedTracksAsString(buffer);
151
152   // get reference to clipboard
153   QClipboard *clipboard = QApplication::clipboard();
154   clipboard->setText(buffer.c_str());
155   emit basepairsCopied(base_pairs_copied);
156 }
157
158 void SequenceBrowser::clear()
159 {
160   GlSeqBrowser::clear();
161   emit tracksChanged();
162 }
163
164 void SequenceBrowser::displayContextMenu(const QPoint& point)
165 {
166   popupMenu->popup(point);
167 }
168
169 void SequenceBrowser::editSequenceProperties()
170 {
171   // if there's a previous window, disconnect its signal
172   if (properties) {
173     disconnect(properties.get(), SIGNAL(propertiesChanged()),
174                this, SLOT(updateGL()));
175   }
176   PropertiesWindowRef new_properties(new PropertiesWindow(track_container));
177   properties = new_properties;
178   connect(properties.get(), SIGNAL(propertiesChanged()),
179           this, SLOT(updateGL()));
180   properties->show();
181 }
182
183 void SequenceBrowser::push_sequence(boost::shared_ptr<Sequence> s)
184 {
185   GlSeqBrowser::push_sequence(s);
186   emit tracksChanged();
187 }
188
189 void SequenceBrowser::push_sequence(boost::shared_ptr<GlSequence> gs)
190 {
191   GlSeqBrowser::push_sequence(gs);
192   emit tracksChanged();
193 }
194
195 ////////////////////
196 // Rendering code
197 void SequenceBrowser::initializeGL()
198 {
199   GlSeqBrowser::initializeGL();
200 }
201
202 void SequenceBrowser::resizeGL(int width, int height)
203 {
204   GlSeqBrowser::resizeGL(width, height);
205   emit viewportChanged();
206 }
207
208 void SequenceBrowser::paintGL()
209 {
210   GlSeqBrowser::paintGL();
211 }
212
213 void SequenceBrowser::mousePressEvent( QMouseEvent *e)
214 {
215   switch(e->button()) {
216     case Qt::LeftButton:
217       startSelecting(e);
218       break;
219    case Qt::RightButton:
220       break;
221    default:
222       break;
223   }
224 }
225
226 void SequenceBrowser::mouseMoveEvent( QMouseEvent *e )
227 {
228   if (rubberBand and rubberBand->isVisible()) {
229     rubberBand->setGeometry(QRect(bandOrigin, e->pos()).normalized());
230   }
231 }
232
233 void SequenceBrowser::mouseReleaseEvent( QMouseEvent *e)
234 {
235   switch(e->button()) {
236     case Qt::LeftButton:
237       stopSelecting(e);
238       break;
239    case Qt::RightButton:
240       // ok so selectedMode and drawing mode should probably be combinded
241       // into a single state variable.
242       if (rubberBand and 
243           not rubberBand->isVisible() and
244           selectedCanvasRegion.contains(e->pos())) {
245         displayContextMenu(e->globalPos());
246       }
247       break;
248    default:
249       break;
250   }
251
252 }
253
254 void SequenceBrowser::startSelecting(QMouseEvent *e)
255 {
256   if (!rubberBand)
257     rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
258
259   if (not rubberBand->isVisible()) {
260     bandOrigin = e->pos();
261     rubberBand->setGeometry(QRect(bandOrigin, QSize()));
262     rubberBand->show();
263   }
264 }
265
266 void SequenceBrowser::stopSelecting(QMouseEvent *e)
267 {
268   if (rubberBand and rubberBand->isVisible()) {
269     rubberBand->hide();
270     selectedMode = false;
271     QRect r = QRect(bandOrigin, e->pos()).normalized();
272     bandOrigin = r.topLeft();
273   
274     selectRegion(r.top(), r.left(), r.bottom(), r.right());
275     selectedCanvasRegion = r;
276   }
277 }
278
279 void SequenceBrowser::wheelEvent(QWheelEvent *e)
280 {
281   e->accept();
282   double cur_zoom = GlSeqBrowser::zoom();
283   
284   // Normalize so one 15 degree turn of the mouse wheel
285   // is equal to 1 step.
286   int num_degrees = e->delta() / 8;
287   int num_steps = num_degrees / 15;
288   
289   // Positive
290   if (num_steps >= 0)
291   {
292     // Invert number of steps because code was written
293     // thinking that adding a positve num_steps was zooming
294     // in, but because it's bp/pixel, it's actually zooming out.
295     // To get mouse wheel to zoom in, when pushing the wheel
296     // forward, I can use the existing code, by making num_steps
297     // negative here.
298     num_steps = num_steps * (-1);
299     
300     if (cur_zoom + num_steps >= 1.0)
301     {
302       emit mouseWheelZoom(cur_zoom + num_steps);
303     }
304     else if (cur_zoom > 1.0 && cur_zoom + num_steps < 1.0)
305     {
306       emit mouseWheelZoom(1.0);
307     }
308     else if (cur_zoom <= 0.1)
309     {
310       emit mouseWheelZoom(cur_zoom + ((double)num_steps*0.01));
311     }
312     else if (cur_zoom <= 1.0 && cur_zoom + ((double)num_steps*0.1) >= 0.1)
313     {
314       emit mouseWheelZoom(cur_zoom + ((double)num_steps*0.1));
315     }
316     else if (cur_zoom <= 1.0 && cur_zoom + ((double)num_steps*0.1) < 0.1)
317     {
318       emit mouseWheelZoom(0.1);
319     }
320     
321   }
322   // Negative
323   else
324   {
325     
326     // Invert number of steps because code was written
327     // thinking that adding a positve num_steps was zooming
328     // in, but because it's bp/pixel, it's actually zooming out.
329     // To get mouse wheel to zoom out, when pulling the wheel
330     // backwards, I can use the existing code, by making num_steps
331     // positive here.
332     num_steps = num_steps * (-1);
333     
334     if (cur_zoom >= 1.0)
335     {
336       emit mouseWheelZoom(cur_zoom+num_steps);
337     }
338     else if (cur_zoom < 1.0 && cur_zoom >= 0.1)
339     {
340       emit mouseWheelZoom(cur_zoom + ((double)num_steps*0.1));
341     }
342     else if (cur_zoom < 0.1)
343     {
344       emit mouseWheelZoom(cur_zoom + ((double)num_steps*0.01));
345     }
346     
347   }
348   
349   //cout << "Mouse wheel delta: " << num_degrees << "; " << num_steps << "\n";
350 }
351
352 void SequenceBrowser::clearSelection()
353 {
354   GlSeqBrowser::clearSelection();
355   if (rubberBand and rubberBand->isVisible()) {
356     rubberBand->hide();
357   }
358   updateGL();
359 }