add copy sequence as string option
[mussa.git] / qui / MussaWindow.cpp
1 #include "py/python.hpp"
2 #include "qui/MussaWindow.hpp"
3 #include "mussa_exceptions.hpp"
4 #include "version.hpp"
5
6 #include <QAction>
7 #include <QApplication>
8 #include <QAssistantClient>
9 #include <QCloseEvent>
10 #include <QDir>
11 #include <QFileDialog>
12 #include <QHBoxLayout>
13 #include <QIcon>
14 #include <QMenuBar>
15 #include <QMessageBox>
16 #include <QScrollBar>
17 #include <QStatusBar>
18 #include <QString>
19 #include <QStringList>
20 #include <QWhatsThis>
21
22 #include <memory>
23 #include <iterator>
24 #include <iostream>
25
26 #include <boost/filesystem/path.hpp>
27 #include <boost/filesystem/operations.hpp>
28 #include <boost/filesystem/convenience.hpp>
29 namespace fs = boost::filesystem;
30 #include <boost/bind.hpp>
31
32 using namespace std;
33
34 MussaWindow::MussaWindow(MussaRef analysis_, QWidget *parent) :
35   QMainWindow(parent),
36   analysis(analysis_),
37   default_dir(new QDir(QDir::home().absolutePath())),
38   motif_editor(0),
39   setup_analysis_dialog(new MussaSetupDialog(this)),
40   subanalysis_window(new SubanalysisWindow(analysis)),
41   browser(new SequenceBrowserWidget(default_dir, this)),  
42   mussaViewTB(new QToolBar("Path Views")),
43   zoom(new ZoomWidget),
44   threshold(new ThresholdWidget),
45   progress_dialog(0),
46   aboutAction(0),
47   closeAction(0),
48   createNewAnalysisAction(0),
49   createSubAnalysisAction(0),
50   editMotifsAction(0),
51   loadMotifListAction(0),
52   loadMupaAction(0),
53   loadSavedAnalysisAction(0),
54   mussaManualAssistantAction(0),
55   newMussaWindowAction(0),
56   saveMotifListAction(0),
57   showMussaViewToolbarAction(0),
58   toggleMotifsAction(0),
59   saveBrowserPixmapAction(0),
60   whatsThisAction(0),
61   viewMussaAlignmentAction(0),
62   manualAssistant(0)
63 {
64   setupActions();
65   setupMainMenu();
66   setupAssistant();
67
68   //This next setWhatsThis function prevents
69   // a segfault when using WhatsThis feature with 
70   // opengl widget.
71   //scene->setWhatsThis(tr("Mussa in OpenGL!"));
72   setCentralWidget(browser);
73   // well updatePosition isn't quite right as we really just need
74   // to call update()
75   connect(this, SIGNAL(changedAnnotations()), browser, SLOT(update()));
76   connect(this, SIGNAL(changedMotifs()), this, SLOT(updateAnnotations()));
77   connect(browser, SIGNAL(basepairsCopied(size_t)), 
78           this, SLOT(showBasePairsCopied(size_t)));
79
80   //mussaViewTB->addAction(toggleMotifsAction);
81   mussaViewTB->addWidget(zoom);
82   
83   connect(zoom, SIGNAL(valueChanged(double)), 
84           browser, SLOT(setZoom(double)));
85   
86   // threshold range is set in updateAnalysis
87   
88   //scene->setClipPlane(20);
89   // FIXME: for when we get the paths drawn at the appropriate depth
90   //connect(threshold, SIGNAL(thresholdChanged(int)),
91   //        this, SLOT(setClipPlane(int)));
92   connect(threshold, SIGNAL(thresholdChanged(int)),
93           this, SLOT(setSoftThreshold(int)));
94   mussaViewTB->addWidget(threshold);
95
96   addToolBar(mussaViewTB);
97
98   statusBar()->showMessage("Welcome to mussa", 2000);
99   // FIXME: we should start refactoring the connect call to updateAnalysis or something
100   if (analysis) {
101     connect(analysis.get(), SIGNAL(progress(const std::string&, int, int)),
102             this, SLOT(updateProgress(const std::string&, int, int)));
103   }
104   updateTitle();
105   updateAnalysis();
106 }
107
108 void MussaWindow::setAnalysis(MussaRef new_analysis)
109 {
110   if (new_analysis != 0) {
111     // only switch mussas if we loaded without error
112     clear();
113     analysis.swap(new_analysis);
114     updateTitle();
115     updateAnalysis();
116   }
117 }
118
119 void MussaWindow::setupActions()
120 {
121   // we really don't want to run this more than once.
122   assert (closeAction == 0);
123
124   // the ever popular about box
125   aboutAction = new QAction(tr("&About"), this);
126   connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
127   aboutAction->setIcon(QIcon(":/icons/info.png"));
128
129   // add exit
130   closeAction = new QAction(tr("&Close"), this);
131   closeAction->setStatusTip(tr("Close this window"));
132   connect(closeAction, SIGNAL(triggered()), this, SLOT(close()));
133   closeAction->setIcon(QIcon(":/icons/exit.png"));
134   
135   createNewAnalysisAction = new QAction(tr("Create &New Analysis"), this);
136   connect(createNewAnalysisAction, SIGNAL(triggered()), 
137           this, SLOT(createNewAnalysis()));
138   createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png"));
139   createNewAnalysisAction->setShortcut(Qt::CTRL | Qt::Key_N);
140   
141   createSubAnalysisAction = new QAction(tr("Add to Subanalysis"), this);
142   connect(createSubAnalysisAction, SIGNAL(triggered()), 
143           this, SLOT(createSubAnalysis()));
144
145   saveAnalysisAction = new QAction(tr("&Save Analysis"), this);
146   connect(saveAnalysisAction, SIGNAL(triggered()), 
147           this, SLOT(saveAnalysis()));
148   saveAnalysisAction->setIcon(QIcon(":/icons/filesave.png"));
149   saveAnalysisAction->setShortcut(Qt::CTRL | Qt::Key_S);
150
151   saveAnalysisAsAction = new QAction(tr("Save Analysis &As"), this);
152   connect(saveAnalysisAsAction, SIGNAL(triggered()), 
153           this, SLOT(saveAnalysisAs()));
154   saveAnalysisAsAction->setIcon(QIcon(":/icons/filesave.png"));
155
156   editMotifsAction = new QAction(tr("Edit Motifs"), this);;
157   connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs()));
158   
159   loadMotifListAction = new QAction(tr("Open Motif List"), this);
160   connect(loadMotifListAction, SIGNAL(triggered()), 
161           this, SLOT(loadMotifList()));
162   loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png"));
163   
164   loadMupaAction = new QAction(tr("Create Analysis from File"), this);
165   connect(loadMupaAction, SIGNAL(triggered()), 
166           this, SLOT(loadMupa()));
167   loadMupaAction->setIcon(QIcon(":/icons/fileopen.png"));
168
169   loadSavedAnalysisAction = new QAction(tr("&Open Existing &Analysis"), this);
170   connect(loadSavedAnalysisAction, SIGNAL(triggered()), 
171           this, SLOT(loadSavedAnalysis()));
172   loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png"));
173   loadSavedAnalysisAction->setShortcut(Qt::CTRL | Qt::Key_O);
174
175   mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this);
176   mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png"));
177   connect(mussaManualAssistantAction, SIGNAL(triggered()),
178           this, SLOT(showManual()));
179
180   newMussaWindowAction = new QAction(tr("&New Mussa Window"), this);
181   newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results");
182   connect(newMussaWindowAction, SIGNAL(triggered()), 
183           this, SLOT(newMussaWindow()));
184   newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png"));
185
186   saveMotifListAction = new QAction(tr("Save Motifs"), this);
187   connect(saveMotifListAction, SIGNAL(triggered()), 
188           this, SLOT(saveMotifList()));
189   saveMotifListAction->setIcon(QIcon(":/icons/filesave.png"));
190
191   showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this);
192   connect(showMussaViewToolbarAction, SIGNAL(triggered()), 
193           this, SLOT(showMussaToolbar()));
194   showMussaViewToolbarAction->setCheckable(true);
195   showMussaViewToolbarAction->setChecked(true);
196
197   toggleMotifsAction = new QAction(tr("Toggle Motifs"), this);
198   connect(toggleMotifsAction, SIGNAL(triggered()), 
199           this, SLOT(toggleMotifs()));
200   toggleMotifsAction->setCheckable(true);
201   toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png"));
202   toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n"
203                                    "You can load motif annotations via "
204                                    "'File->Load Motif List' menu option."));
205
206   //Save pixel map action
207   saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
208   connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
209           browser, SLOT(promptSaveBrowserPixmap()));
210   saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
211
212   viewMussaAlignmentAction = new QAction(tr("View sequence alignment"), this);
213   connect(viewMussaAlignmentAction, SIGNAL(triggered()),
214           this, SLOT(viewMussaAlignment() ));
215   viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window "
216                                             "showing alignment of the seqcomp "
217                                             "defined paths"));
218
219   whatsThisAction = QWhatsThis::createAction(this);
220   whatsThisAction->setIcon(QIcon(":/icons/help.png"));
221
222
223 }
224
225 void MussaWindow::closeEvent(QCloseEvent *event)
226 {
227   if(isClearingAnalysisSafe()) {
228     event->accept();
229   } else {
230     event->ignore();
231   }
232 }
233
234 void MussaWindow::setupMainMenu()
235 {
236   // we need to run setupActions first
237   assert (closeAction != 0);
238
239   QMenu *newMenu = menuBar()->addMenu(tr("&File"));
240   
241   newMenu->addAction(newMussaWindowAction);
242   newMenu->addAction(createNewAnalysisAction);
243   newMenu->addAction(loadMupaAction);
244   newMenu->addAction(loadSavedAnalysisAction);
245   newMenu->addAction(saveAnalysisAction);
246   newMenu->addAction(saveAnalysisAsAction);
247   newMenu->addSeparator();
248   newMenu->addAction(loadMotifListAction);
249   newMenu->addAction(saveMotifListAction);
250   newMenu->addSeparator();
251   newMenu->addAction(saveBrowserPixmapAction);
252   newMenu->addSeparator();
253   newMenu->addAction(closeAction);
254
255   newMenu = menuBar()->addMenu(tr("&Edit"));
256   newMenu->addAction(editMotifsAction);
257   newMenu->addAction(browser->getCopySelectedSequenceAsStringAction());
258   newMenu->addAction(browser->getCopySelectedSequenceAsFastaAction());
259   newMenu->addAction(createSubAnalysisAction);
260  
261   newMenu = menuBar()->addMenu(tr("&View"));
262   newMenu->addAction(viewMussaAlignmentAction);
263   newMenu->addAction(showMussaViewToolbarAction);
264
265   newMenu = menuBar()->addMenu(tr("&Help"));
266   newMenu->addAction(mussaManualAssistantAction);
267   newMenu->addAction(whatsThisAction);
268   newMenu->addSeparator();
269   newMenu->addAction(aboutAction);
270
271   // add some extra features to the context menu
272   QMenu *popupMenu = browser->getPopupMenu();
273   if (popupMenu) {
274     popupMenu->addAction(viewMussaAlignmentAction);
275     popupMenu->addAction(createSubAnalysisAction);
276   }
277 }
278
279 void MussaWindow::setupAssistant()
280 {
281 #if defined(QT_QTASSISTANT_FOUND)
282   QStringList manualAssistantArgs;
283   manualAssistantArgs = QStringList();
284   manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
285   manualAssistant = new QAssistantClient("assistant", this);
286   manualAssistant->setArguments(manualAssistantArgs);
287   connect(manualAssistant, SIGNAL(error(QString)),
288           this, SLOT(assistantError(QString)));
289 #endif
290 }
291   
292 void MussaWindow::about()
293 {
294   QString msg;
295   msg += "Welcome to Multiple Species Sequence Analysis\n";
296   msg += "(c) 2005-2006 California Institute of Technology\n";
297   msg += "Diane Trout, Tristan De Buysscher, Brandon King\n";
298   msg += "Version: ";
299   msg += mussa_version;
300   msg += "\n";
301   msg += "OpenGL: ";
302   msg += (char *)glGetString(GL_VERSION);
303   msg += "\n";
304   QMessageBox::about(this, tr("About mussa"), msg);
305 }
306
307 void MussaWindow::clear()
308 {
309   aligned_windows.clear();
310   browser->clear();
311 }
312
313 void MussaWindow::createNewAnalysis()
314 {
315   try {
316     // ideally we should open a new window if there's an analysis
317     // but this should work for the moment.
318     if (not isClearingAnalysisSafe()) return;
319
320     if (setup_analysis_dialog->exec()) {
321       setAnalysis(setup_analysis_dialog->getMussa());
322     }
323   } catch(mussa_error e) {
324     QString msg(e.what());
325     QMessageBox::warning(this, tr("Create New Analysis"), msg);
326   }
327 }
328
329 void MussaWindow::createSubAnalysis()
330 {
331   list<SequenceLocation> result;
332   SequenceLocationModel& model = subanalysis_window->getModel();
333   browser->copySelectedTracksAsSeqLocation(result);
334   for(list<SequenceLocation>::iterator result_itor = result.begin();
335       result_itor != result.end();
336       ++result_itor)
337   {
338     model.push_back(*result_itor);
339   }
340
341   if (not subanalysis_window->isVisible()) {
342     subanalysis_window->show();
343   }
344 }
345
346 void MussaWindow::saveAnalysis()
347 {
348   // if we've got an analysis
349   if (analysis and not analysis->empty()) {
350     // if it doesn't have a name we need to pick one
351     if (analysis->get_analysis_path().empty()) {
352       saveAnalysisAs();
353     } else {     
354       // if we've got a name, has it changed any?
355       // this doesn't work when a sequence changes something (like its
356       // name)
357       //else if (analysis->is_dirty())
358       try { 
359         analysis->save();
360       } catch (std::exception e) {
361         QMessageBox::critical(this, 
362                               tr("Mussa Save Error"),
363                               tr(e.what()),
364                               QMessageBox::Ok, 0, 0);
365   }
366       
367     }
368   }
369 }
370
371 void MussaWindow::saveAnalysisAs()
372 {
373   std::auto_ptr<QFileDialog> dialog(new QFileDialog(this));
374   dialog->setAcceptMode(QFileDialog::AcceptSave);
375   dialog->setFileMode(QFileDialog::AnyFile);
376   dialog->setDirectory(*default_dir);
377
378   QStringList fileNames;
379   if (not dialog->exec()) {
380     return;
381   }
382   fileNames = dialog->selectedFiles();
383    
384   if (fileNames.size() != 1) {
385     return;
386   }
387   try {
388     fs::path save_path(fileNames[0].toStdString(), fs::native);
389     // do you want to overwrite?
390     if (fs::exists(save_path) and 
391         QMessageBox::question(
392           this,
393           tr("Overwrite File? -- Mussa"),
394           tr("A file called %1 already exists"
395              "do you want to overwrite it?")
396              .arg(fileNames[0]),
397           tr("&Yes"), tr("&No"),
398           QString(), 0, 1)
399         ) {
400       return;
401     }
402     analysis->save(save_path);
403     fs::path normalized_path = (save_path / "..").normalize();   
404     default_dir->setPath(normalized_path.native_directory_string().c_str());
405   } catch (std::exception e) {
406     QMessageBox::critical(this, 
407                           tr("Mussa Save Error"),
408                           tr(e.what()),
409                           QMessageBox::Ok, 0, 0);
410   }
411 }
412
413 bool MussaWindow::isClearingAnalysisSafe()
414 {
415   if (analysis and not analysis->empty() and analysis->is_dirty()) {
416     switch (QMessageBox::question(
417       this,
418       tr("Save Unsaved Changes -- Mussa"),
419       tr("There are unsaved changes,\ndo you want to save?"),
420       tr("&Yes"), tr("&No"), tr("&Cancel"),
421       0, 2)) {
422     case 0:
423       // save
424       saveAnalysis();
425       break;
426     case 1:
427       // don't save
428       break;
429     case 2:
430       // don't replace 
431       return false;
432       break;
433     default:
434       // error
435       throw runtime_error("isClearingAnalysis QMesageBox failure");
436     }
437   } 
438   // if we're here we've been saved and can replace
439   return true;
440 }
441
442 void MussaWindow::editMotifs()
443 {
444   if (motif_editor != 0) {
445     motif_editor->hide();
446     delete motif_editor;
447   }
448   motif_editor = new MotifEditor(analysis);
449   connect(motif_editor, SIGNAL(changedMotifs()), 
450           this, SLOT(updateAnnotations()));
451   motif_editor->show();
452 }
453
454 void MussaWindow::loadMotifList()
455 {
456   QString caption("Mussa Load Motifs");
457   QString filter("Motif list(*.txt *.mtl)");
458   QString path = QFileDialog::getOpenFileName(this,
459                                               caption, 
460                                               default_dir->absolutePath(),
461                                               filter);
462   // user hit cancel?
463   if (path.isNull()) 
464     return;
465   // try to load safely
466   try {
467     fs::path converted_path(path.toStdString(), fs::native);
468     analysis->load_motifs(converted_path);
469     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
470     emit changedMotifs();
471   } catch (std::exception e) {
472     QString msg("Unable to load ");
473     msg += path;
474     msg += "\n";
475     msg += e.what();
476     QMessageBox::warning(this, caption, msg);
477   }
478 }
479
480 void MussaWindow::saveMotifList()
481 {
482   QString caption("Mussa Save Motifs");
483   QString filter("Motif list(*.txt *.mtl)");
484   QString path = QFileDialog::getSaveFileName(this,
485                                               caption, 
486                                               default_dir->absolutePath(),
487                                               filter);
488   // user hit cancel?
489   if (path.isNull()) 
490     return;
491   // try to load safely
492   try {
493     fs::path converted_path(path.toStdString(), fs::native);
494     if (fs::extension(converted_path).size() == 0) {
495       // no extension, so add one
496       fs::path base_path = converted_path.branch_path();
497       fs::path filename(converted_path.leaf() + ".mtl", fs::native);
498       converted_path = base_path / filename;
499     }
500     analysis->save_motifs(converted_path);
501     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
502   } catch (std::exception e) {
503     QString msg("Unable to save ");
504     msg += path;
505     msg += "\n";
506     msg += e.what();
507     QMessageBox::warning(this, caption, msg);
508   }}
509
510 void MussaWindow::loadMupa()
511 {
512   QString caption("Load a mussa parameter file");
513   QString filter("Mussa Parameters (*.mupa)");
514   QString mupa_path = QFileDialog::getOpenFileName(
515                          this,
516                          caption, 
517                          default_dir->absolutePath(),
518                          filter
519                       );
520   // user hit cancel?
521   if (mupa_path.isNull()) 
522     return;
523   // try to load safely
524   try {
525     // ideally we should open a new window if there's an analysis
526     // but this should work for the moment.
527     if (not isClearingAnalysisSafe()) return;
528
529     MussaRef m(new Mussa);
530     fs::path converted_path(mupa_path.toStdString(), fs::native);
531     connect(m.get(), SIGNAL(progress(const std::string&, int, int)),
532             this, SLOT(updateProgress(const std::string&, int, int)));
533     m->load_mupa_file(converted_path);
534     m->analyze();
535     setAnalysis(m);
536     updateTitle();
537     // grab the path ignoring the mupa file portion
538     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
539   } catch (mussa_load_error e) {
540     QString msg("Unable to load ");
541     msg += mupa_path;
542     msg += "\n";
543     msg += e.what();
544     QMessageBox::warning(this, "Load Parameter", msg);
545   }
546   assert (analysis != 0);
547 }
548
549 void MussaWindow::loadSavedAnalysis()
550 {
551   QString caption("Load a previously run analysis");
552   QString muway_dir = QFileDialog::getExistingDirectory(
553                         this,
554                         caption, 
555                         default_dir->absolutePath()
556                       );
557   // user hit cancel?
558   if (muway_dir.isNull()) 
559     return;
560   // try to safely load
561   try {
562     // ideally we should open a new window if there's an analysis
563     // but this should work for the moment.
564     if (not isClearingAnalysisSafe()) return;
565
566     MussaRef m(new Mussa);
567     fs::path converted_path(muway_dir.toStdString(), fs::native);
568     connect(m.get(), SIGNAL(progress(const std::string&, int, int)),
569             this, SLOT(updateProgress(const std::string&, int, int)));
570     m->load(converted_path);
571     // only switch mussas if we loaded without error
572     if (analysis->empty()) {
573       // our current window is empty so load and replace.
574       setAnalysis(m);
575       updateTitle();
576       default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
577     } else {
578       MussaWindow *win = new MussaWindow(m);
579       updateTitle();
580       win->default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
581       win->show();
582     }
583   } catch (mussa_load_error e) {
584     QString msg("Unable to load ");
585     msg += muway_dir;
586     msg += "\n";
587     msg += e.what();
588     QMessageBox::warning(this, "Load Parameter", msg);
589   }
590   assert (analysis != 0);
591 }
592
593 void MussaWindow::newMussaWindow()
594 {
595   MussaRef a(new Mussa);
596   MussaWindow *win = new MussaWindow(a);
597   win->default_dir = default_dir;
598   win->show();
599 }
600
601 void MussaWindow::setSoftThreshold(int value)
602 {
603   if (analysis->get_soft_threshold() != value) {
604     threshold->setEnabled( false );
605     analysis->set_soft_threshold(value);
606     analysis->nway();
607     updateLinks();
608     update();
609     threshold->setEnabled( true );
610   }
611 }
612
613 void MussaWindow::showMussaToolbar()
614 {
615   if (mussaViewTB->isVisible())
616     mussaViewTB->hide();
617   else
618     mussaViewTB->show();
619 }
620
621 void MussaWindow::showBasePairsCopied(size_t bp_copied)
622 {
623   QString msg("Copied ");
624   QString num;
625   num.setNum(bp_copied);
626   msg += num + " base pairs";
627   statusBar()->showMessage(msg, 5000);
628 }
629
630
631 void MussaWindow::toggleMotifs()
632 {
633   NotImplementedBox();
634 }
635
636 void MussaWindow::showManual()
637 {
638 #if defined(QT_QTASSISTANT_FOUND)
639   if (manualAssistant) { 
640     manualAssistant->openAssistant();
641   } else {
642     QMessageBox::warning(this,
643                          tr("Mussa Help Error"),
644                          tr("QtAssistant not setup correctly"),
645                          QMessageBox::Ok,
646                          QMessageBox::NoButton,
647                          QMessageBox::NoButton);
648   }
649 #else
650   try {
651     boost::python::object webopen = get_py()["webbrowser.open"];
652     webopen("http://woldlab.caltech.edu/~king/mussagl_manual/");
653   } catch( boost::python::error_already_set ) {
654     PyErr_Print();
655     QMessageBox::warning(this,
656                          tr("Mussa Help Error"),
657                          tr("Unable to launch webbrowser"),
658                          QMessageBox::Ok,
659                          QMessageBox::NoButton,
660                          QMessageBox::NoButton);
661   }
662 #endif //QT_QTASSISTANT_FOUND
663 }
664
665 void MussaWindow::assistantError(QString message)
666 {
667   //std::cout << "QAssistantError: " << message.toStdString() << "\n";
668   QMessageBox::warning ( this, "Warning: Mussagl Manual", message, 
669                          QMessageBox::Ok, 
670                          QMessageBox::NoButton);
671 }
672
673 void MussaWindow::NotImplementedBox()
674 {
675   QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
676 }      
677
678 void MussaWindow::viewMussaAlignment()
679 {
680   const set<int>& selected_paths = browser->selectedPaths();
681   if (selected_paths.size() == 0 ) {
682     QMessageBox::warning(this, 
683                          QObject::tr("mussa"),
684                          QObject::tr("you should probably select some paths "
685                                      "first"));
686   } else {
687     MussaAlignedWindowRef ma_win( 
688       new MussaAlignedWindow(analysis, default_dir, selected_paths, subanalysis_window)
689     );
690
691     aligned_windows.push_back(ma_win);
692     connect(this, SIGNAL(changedAnnotations()), 
693             aligned_windows.back().get(), SLOT(update()));
694     aligned_windows.back()->show();
695   }
696 }
697                         
698 void MussaWindow::updateAnalysis()
699 {
700   const Mussa::vector_sequence_type& seqs = analysis->sequences();
701   browser->setSequences(seqs, analysis->colorMapper());
702   assert(browser->sequences().size() == analysis->size());
703
704   // setRange eventually emits something that causes updateLinks to be called
705   // but it's possible for us to not have had a chance to set out sequences
706   // yet.
707   threshold->setRange(analysis->get_threshold(),analysis->get_window());
708   updateLinks();
709   browser->zoomOut();
710   zoom->setValue(browser->zoom());
711 }
712
713 void MussaWindow::updateAnnotations()
714 {
715   // motifs were changed in the sequences by 
716   // Mussa::update_sequences_motifs
717   emit changedAnnotations();
718   browser->update();
719 }
720
721 void MussaWindow::updateLinks()
722 {
723   if(browser->sequences().size() == 0) {
724     // we don't have any sequences load so we have no business setting links
725     return;
726   }
727
728   browser->clear_links();
729   bool reversed = false;
730   const NwayPaths& nway = analysis->paths();
731
732   typedef list<ConservedPath> conserved_paths;
733   typedef conserved_paths::const_iterator const_conserved_paths_itor;
734   for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
735       path_itor != nway.refined_pathz.end();
736       ++path_itor)
737   {
738     // since we were drawing to the start of a window, and opengl lines
739     // are centered around the two connecting points our lines were slightly
740     // offset. the idea of window_offset is to adjust them to the
741     // right for forward compliment or left for reverse compliment
742     // FIXME: figure out how to unit test these computations
743     //GLfloat window_offset = (path_itor->window_size)/2.0;
744
745     size_t track_len = path_itor->track_indexes.size();
746     vector<int> normalized_path;
747     normalized_path.reserve(track_len);
748     vector<bool> rc_flags(false, track_len);
749     for (size_t track_i=0; track_i != track_len; ++track_i)
750     {
751       int x = path_itor->track_indexes[track_i];
752       // at some point when we modify the pathz data structure to keep
753       // track of the score we can put grab the depth here.
754       //
755       // are we reverse complimented?
756       if ( x>=0) {
757         reversed = false;
758       } else {
759         reversed = true;
760         // make positive 
761         x = -x;
762       }
763       normalized_path.push_back(x);
764       rc_flags.push_back(reversed);
765     }
766     browser->link(normalized_path, rc_flags, path_itor->window_size);
767   }
768   browser->update();
769 }
770
771 void 
772 MussaWindow::updateProgress(const string& description, int current, int max)
773 {  
774   // if we're done  
775   if (current == max) {
776     if (progress_dialog != 0) {
777       progress_dialog->hide();
778       delete progress_dialog;
779       progress_dialog = 0;
780     }
781   } else {
782     // if we're starting, create the dialog
783     if (progress_dialog == 0) {
784       QString desc(description.c_str());
785       QString cancel("Cancel");
786       progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
787       progress_dialog->show();
788     } else {
789       // just update the dialog
790       progress_dialog->setValue(current);
791     }
792   }
793   qApp->processEvents();
794 }
795
796 void MussaWindow::updateTitle()
797 {
798   if (analysis) {
799     setWindowTitle(analysis->get_title().c_str());
800   }
801 }