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