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