simplify zoom code
[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       e->accept();
219       break;
220    default:
221       e->ignore();
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     e->accept();
231   } else {
232     e->ignore();
233   }
234 }
235
236 void SequenceBrowser::mouseReleaseEvent( QMouseEvent *e)
237 {
238   switch(e->button()) {
239     case Qt::LeftButton:
240       stopSelecting(e);
241       break;
242    case Qt::RightButton:
243       // ok so selectedMode and drawing mode should probably be combinded
244       // into a single state variable.
245       if (rubberBand and 
246           not rubberBand->isVisible() and
247           selectedCanvasRegion.contains(e->pos())) {
248         displayContextMenu(e->globalPos());
249       }
250       break;
251    default:
252       e->ignore();
253       return;
254       break;
255   }
256   e->accept();
257 }
258
259 void SequenceBrowser::startSelecting(QMouseEvent *e)
260 {
261   if (!rubberBand)
262     rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
263
264   if (not rubberBand->isVisible()) {
265     bandOrigin = e->pos();
266     rubberBand->setGeometry(QRect(bandOrigin, QSize()));
267     rubberBand->show();
268   }
269 }
270
271 void SequenceBrowser::stopSelecting(QMouseEvent *e)
272 {
273   if (rubberBand and rubberBand->isVisible()) {
274     rubberBand->hide();
275     selectedMode = false;
276     QRect r = QRect(bandOrigin, e->pos()).normalized();
277     bandOrigin = r.topLeft();
278   
279     selectRegion(r.top(), r.left(), r.bottom(), r.right());
280     selectedCanvasRegion = r;
281   }
282 }
283
284 void SequenceBrowser::wheelEvent(QWheelEvent *e)
285 {
286   e->accept();
287   double cur_zoom = GlSeqBrowser::zoom();
288   
289   // Normalize so one turn of the mouse wheel
290   // is equal to 1 step.
291   const int normalize_tick = 120;
292   // arbitrary scaling factor that seems to "work"
293   const int scaling = 50;
294   int num_steps = e->delta() / normalize_tick;
295
296   cur_zoom = pow(10, log10(cur_zoom) - ((double)num_steps/scaling));
297   emit mouseWheelZoom(cur_zoom);
298 }
299
300 void SequenceBrowser::clearSelection()
301 {
302   GlSeqBrowser::clearSelection();
303   if (rubberBand and rubberBand->isVisible()) {
304     rubberBand->hide();
305   }
306   updateGL();
307 }