7ffbadc5023dfa5546ec3f69d183b86bcfb80b3b
[mussa.git] / qui / PathWindow.cpp
1 #include <QAction>
2 #include <QDir>
3 #include <QFileDialog>
4 #include <QHBoxLayout>
5 #include <QIcon>
6 #include <QMenuBar>
7 #include <QMessageBox>
8 #include <QScrollBar>
9 #include <QStatusBar>
10 #include <QString>
11 #include <QWhatsThis>
12
13 #include "qui/PathScene.hpp"
14 #include "qui/PathWindow.hpp"
15 #include "qui/ImageSaveDialog.hpp"
16 #include "mussa_exceptions.hpp"
17
18 #include <iostream>
19
20 using namespace std;
21
22 PathWindow::PathWindow(Mussa *analysis_, QWidget *parent) :
23   QMainWindow(parent),
24   analysis(analysis_),
25   path_view(this),
26   mussaViewTB("Path Views"),
27   zoomBox(),
28   threshold(),
29   closeAction(0) // initialize one of the pointers to null as a saftey flag
30 {
31   setupActions();
32   setupMainMenu();
33
34   //This next setWhatsThis function prevents
35   // a segfault when using WhatsThis feature with 
36   // opengl widget.
37   //scene->setWhatsThis(tr("Mussa in OpenGL!"));
38   setCentralWidget(&path_view);
39
40   mussaViewTB.addAction(toggleMotifsAction);
41
42   zoomBox.setWhatsThis("zoom magnification factor");
43   zoomBox.setRange(2,1000);
44   mussaViewTB.addWidget(&zoomBox);
45   connect(&zoomBox, SIGNAL(valueChanged(int)), 
46           &path_view.scene, SLOT(setZoom(int)));
47   
48   threshold.setRange(19, 30);
49   threshold.setThreshold(19);
50   //scene->setClipPlane(20);
51   // FIXME: for when we get the paths drawn at the appropriate depth
52   //connect(threshold, SIGNAL(thresholdChanged(int)),
53   //        scene, SLOT(setClipPlane(int)));
54   //connect(&threshold, SIGNAL(thresholdChanged(int)),
55   //        &scene, SLOT(setSoftThreshold(int)));
56   mussaViewTB.addWidget(&threshold);
57
58   addToolBar(&mussaViewTB);
59
60   statusBar()->showMessage("Welcome to mussa", 2000);
61   updateAnalysis();
62 }
63
64 void PathWindow::setupActions()
65 {
66   // we really don't want to run this more than once.
67   assert (closeAction == 0);
68
69   // the ever popular about box
70   aboutAction = new QAction(tr("&About"), this);
71   connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
72   aboutAction->setIcon(QIcon("icons/info.png"));
73
74   // add exit
75   closeAction = new QAction(tr("&Close"), this);
76   closeAction->setStatusTip(tr("Close this window"));
77   connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
78   closeAction->setIcon(QIcon("icons/exit.png"));
79   
80   createNewAnalysisAction = new QAction(tr("Define Analysis"), this);
81   connect(createNewAnalysisAction, SIGNAL(triggered()), 
82           this, SLOT(createNewAnalysis()));
83   createNewAnalysisAction->setIcon(QIcon("icons/filenew.png"));
84   
85   createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this);
86   connect(createSubAnalysisAction, SIGNAL(triggered()), 
87           this, SLOT(createSubAnalysis()));
88
89   loadMotifListAction = new QAction(tr("Load Motif List"), this);
90   connect(loadMotifListAction, SIGNAL(triggered()), 
91           this, SLOT(loadMotifList()));
92   
93   loadMupaAction = new QAction(tr("Load Mussa Parameters"), this);
94   connect(loadMupaAction, SIGNAL(triggered()), 
95           this, SLOT(loadMupa()));
96
97   loadSavedAnalysisAction = new QAction(tr("Load &Analysis"), this);
98   connect(loadSavedAnalysisAction, SIGNAL(triggered()), 
99           this, SLOT(loadSavedAnalysis()));
100   loadSavedAnalysisAction->setIcon(QIcon("icons/fileopen.png"));
101
102   saveMotifListAction = new QAction(tr("Save Motifs"), this);
103   connect(saveMotifListAction, SIGNAL(triggered()), 
104           this, SLOT(saveMotifList()));
105   saveMotifListAction->setIcon(QIcon("icons/filesave.png"));
106
107   showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
108   connect(showMussaViewToolbarAction, SIGNAL(triggered()), 
109           this, SLOT(showMussaToolbar()));
110   showMussaViewToolbarAction->setCheckable(true);
111   showMussaViewToolbarAction->setChecked(true);
112
113   toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
114   connect(toggleMotifsAction, SIGNAL(triggered()), 
115           this, SLOT(toggleMotifs()));
116   toggleMotifsAction->setCheckable(true);
117   toggleMotifsAction->setIcon(QIcon("icons/motif_icon.png"));
118   toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
119                                    "You can load motif annotations via "
120                                    "'File->Load Motif List' menu option."));
121
122   whatsThisAction = QWhatsThis::createAction(this);
123   whatsThisAction->setIcon(QIcon("icons/help.png"));
124
125   //Save pixel map action
126   saveOpenGlPixmapAction = new QAction(tr("Save to image..."), this);
127   connect(saveOpenGlPixmapAction, (SIGNAL(triggered())),
128           this, SLOT(promptSaveOpenGlPixmap()));
129   saveOpenGlPixmapAction->setIcon(QIcon("icons/image2.png"));
130 }
131
132 void PathWindow::setupMainMenu()
133 {
134   // we need to run setupActions first
135   assert (closeAction != 0);
136   
137   QMenu *newMenu;
138   newMenu = menuBar()->addMenu(tr("&File"));
139   newMenu->addAction(createNewAnalysisAction);
140   newMenu->addAction(loadMupaAction);
141   newMenu->addAction(loadSavedAnalysisAction);
142   newMenu->addAction(createSubAnalysisAction);
143   newMenu->addSeparator();
144   newMenu->addAction(loadMotifListAction);
145   newMenu->addAction(saveMotifListAction);
146   newMenu->addSeparator();
147   newMenu->addAction(saveOpenGlPixmapAction);
148   newMenu->addSeparator();
149   newMenu->addAction(closeAction);
150
151   newMenu = menuBar()->addMenu(tr("&View"));
152   newMenu->addAction(showMussaViewToolbarAction);
153
154   newMenu = menuBar()->addMenu(tr("&Help"));
155   newMenu->addAction(whatsThisAction);
156   newMenu->addSeparator();
157   newMenu->addAction(aboutAction);
158 }
159   
160 void PathWindow::about()
161 {
162   QMessageBox::about(this, tr("About mussa"),
163       tr("Welcome to Multiple Species Sequence Analysis\n"
164          "(c) 2005-2006 California Institute of Technology\n"
165          "Tristan De Buysscher, Diane Trout\n"));
166 }
167
168 void PathWindow::createNewAnalysis()
169 {
170   NotImplementedBox();
171 }
172
173 void PathWindow::createSubAnalysis()
174 {
175   NotImplementedBox();
176 }
177
178
179 void PathWindow::loadMotifList()
180 {
181   QString caption("Load a motif list");
182   QString filter("Motif list(*.txt *.mtl)");
183   QString path = QFileDialog::getOpenFileName(this,
184                                                    caption, 
185                                                    QDir::currentPath(),
186                                                    filter);
187   // user hit cancel?
188   if (path.isNull()) 
189     return;
190   // try to load safely
191   try {
192     analysis->load_motifs(path.toStdString());
193   } catch (runtime_error e) {
194     QString msg("Unable to load ");
195     msg += path;
196     msg += "\n";
197     msg += e.what();
198     QMessageBox::warning(this, "Load Motifs", msg);
199   }
200   assert (analysis != 0);
201 }
202
203 void PathWindow::saveMotifList()
204 {
205   NotImplementedBox();
206 }
207
208 void PathWindow::loadMupa()
209 {
210   QString caption("Load a mussa parameter file");
211   QString filter("Mussa Parameters (*.mupa)");
212   QString mupa_path = QFileDialog::getOpenFileName(this,
213                                                    caption, 
214                                                    QDir::currentPath(),
215                                                    filter);
216   // user hit cancel?
217   if (mupa_path.isNull()) 
218     return;
219   // try to load safely
220   try {
221     Mussa *m = new Mussa;
222     m->load_mupa_file(mupa_path.toStdString());
223     m->analyze(0, 0, Mussa::TransitiveNway, 0.0);
224     // only switch mussas if we loaded without error
225     delete analysis;
226     analysis = m;
227     updateAnalysis();
228   } catch (mussa_load_error e) {
229     QString msg("Unable to load ");
230     msg += mupa_path;
231     msg += "\n";
232     msg += e.what();
233     QMessageBox::warning(this, "Load Parameter", msg);
234   }
235   assert (analysis != 0);
236 }
237
238 void PathWindow::loadSavedAnalysis()
239 {
240   QString caption("Load a previously run analysis");
241   QString muway_dir = QFileDialog::getExistingDirectory(this,
242                                                         caption, 
243                                                         QDir::currentPath());
244   // user hit cancel?
245   if (muway_dir.isNull()) 
246     return;
247   // try to safely load
248   try {
249     Mussa *m = new Mussa;
250     m->load(muway_dir.toStdString());
251     // only switch mussas if we loaded without error
252     delete analysis;
253     analysis = m;
254     updateAnalysis();
255   } catch (mussa_load_error e) {
256     QString msg("Unable to load ");
257     msg += muway_dir;
258     msg += "\n";
259     msg += e.what();
260     QMessageBox::warning(this, "Load Parameter", msg);
261   }
262   assert (analysis != 0);
263 }
264
265 void PathWindow::setSoftThreshold(int threshold)
266 {
267   if (analysis->get_threshold() != threshold) {
268     analysis->set_soft_thres(threshold);
269     analysis->nway();
270     updateLinks();
271     update();
272   }
273 }
274
275 void PathWindow::showMussaToolbar()
276 {
277   if (mussaViewTB.isVisible())
278     mussaViewTB.hide();
279   else
280     mussaViewTB.show();
281 }
282
283 void PathWindow::toggleMotifs()
284 {
285   NotImplementedBox();
286 }
287
288 void PathWindow::NotImplementedBox()
289 {
290   QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
291 }      
292
293 void PathWindow::promptSaveOpenGlPixmap()
294 {
295   QSize size;
296   size = path_view.scene.size();
297   //Image Save Dialog
298   ImageSaveDialog imageSaveDialog(&path_view.scene, this);
299   imageSaveDialog.setSize(size.width(), size.height());
300   int result = imageSaveDialog.exec();
301   cout << "Result: " << result << "\n";
302 }
303
304 void PathWindow::updateAnalysis()
305 {
306   cout << "analysis updated" << endl;
307   path_view.scene.clear();
308   const vector<Sequence>& seqs = analysis->sequences();
309   for(vector<Sequence>::const_iterator seq_i = seqs.begin();
310       seq_i != seqs.end();
311       ++seq_i)
312   {
313     path_view.scene.push_sequence(*seq_i);
314   }
315   updateLinks();
316 }
317
318 void PathWindow::updateLinks()
319 {
320   path_view.scene.clear_links();
321   bool reversed = false;
322   const NwayPaths& nway = analysis->paths();
323
324   typedef list<ExtendedConservedPath> conserved_paths;
325   typedef conserved_paths::const_iterator const_conserved_paths_itor;
326   for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
327       path_itor != nway.refined_pathz.end();
328       ++path_itor)
329   {
330     // since we were drawing to the start of a window, and opengl lines
331     // are centered around the two connecting points our lines were slightly
332     // offset. the idea of window_offset is to adjust them to the
333     // right for forward compliment or left for reverse compliment
334     // FIXME: figure out how to unit test these computations
335     //GLfloat window_offset = (path_itor->window_size)/2.0;
336
337     size_t track_len = path_itor->track_indexes.size();
338     vector<int> normalized_path;
339     normalized_path.reserve(track_len);
340     vector<bool> rc_flags(false, track_len);
341     for (size_t track_i=0; track_i != track_len; ++track_i)
342     {
343       int x = path_itor->track_indexes[track_i];
344       // at some point when we modify the pathz data structure to keep
345       // track of the score we can put grab the depth here.
346       //
347       // are we reverse complimented?
348       if ( x>=0) {
349         reversed = false;
350       } else {
351         reversed = true;
352         x = -x; // make positive
353       }
354       normalized_path.push_back(x);
355       rc_flags.push_back(reversed);
356     }
357     path_view.scene.link(normalized_path, rc_flags, path_itor->window_size);
358   }
359 }
360