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