more fs::native for user supplied paths
[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 std::string&, int, int)),
103             this, SLOT(updateProgress(const std::string&, 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  
264   newMenu = menuBar()->addMenu(tr("&View"));
265   newMenu->addAction(viewMussaAlignmentAction);
266   newMenu->addAction(showMussaViewToolbarAction);
267
268   newMenu = menuBar()->addMenu(tr("&Help"));
269   newMenu->addAction(mussaManualAssistantAction);
270   newMenu->addAction(whatsThisAction);
271   newMenu->addSeparator();
272   newMenu->addAction(aboutAction);
273
274   // add some extra features to the context menu
275   QMenu *popupMenu = browser->getPopupMenu();
276   if (popupMenu) {
277     popupMenu->addAction(viewMussaAlignmentAction);
278     popupMenu->addAction(createSubAnalysisAction);
279   }
280 }
281
282 void MussaWindow::setupAssistant()
283 {
284 #if defined(QT_QTASSISTANT_FOUND)
285   QStringList manualAssistantArgs;
286   manualAssistantArgs = QStringList();
287   manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
288   manualAssistant = new QAssistantClient("assistant", this);
289   manualAssistant->setArguments(manualAssistantArgs);
290   connect(manualAssistant, SIGNAL(error(QString)),
291           this, SLOT(assistantError(QString)));
292 #endif
293 }
294   
295 void MussaWindow::about()
296 {
297   QString msg;
298   msg += "Welcome to Multiple Species Sequence Analysis\n";
299   msg += "(c) 2005-2006 California Institute of Technology\n";
300   msg += "Diane Trout, Tristan De Buysscher, Brandon King\n";
301   msg += "Version: ";
302   msg += mussa_version;
303   msg += "\n";
304   msg += "OpenGL: ";
305   msg += (char *)glGetString(GL_VERSION);
306   msg += "\n";
307   QMessageBox::about(this, tr("About mussa"), msg);
308 }
309
310 void MussaWindow::clear()
311 {
312   aligned_windows.clear();
313   browser->clear();
314 }
315
316 void MussaWindow::createNewAnalysis()
317 {
318   try {
319     // ideally we should open a new window if there's an analysis
320     // but this should work for the moment.
321     if (not isClearingAnalysisSafe()) return;
322
323     if (setup_analysis_dialog->exec()) {
324       setAnalysis(setup_analysis_dialog->getMussa());
325     }
326   } catch(mussa_error e) {
327     QString msg(e.what());
328     QMessageBox::warning(this, tr("Create New Analysis"), msg);
329   }
330 }
331
332 void MussaWindow::createSubAnalysis()
333 {
334   list<SequenceLocation> result;
335   SequenceLocationModel& model = subanalysis_window->getModel();
336   browser->copySelectedTracksAsSeqLocation(result);
337   for(list<SequenceLocation>::iterator result_itor = result.begin();
338       result_itor != result.end();
339       ++result_itor)
340   {
341     model.push_back(*result_itor);
342   }
343
344   if (not subanalysis_window->isVisible()) {
345     subanalysis_window->show();
346   }
347 }
348
349 void MussaWindow::saveAnalysis()
350 {
351   // if we've got an analysis
352   if (analysis and not analysis->empty()) {
353     // if it doesn't have a name we need to pick one
354     if (analysis->get_analysis_path().empty()) {
355       saveAnalysisAs();
356     } else {     
357       // if we've got a name, has it changed any?
358       // this doesn't work when a sequence changes something (like its
359       // name)
360       //else if (analysis->is_dirty())
361       try { 
362         analysis->save();
363       } catch (std::exception e) {
364         QMessageBox::critical(this, 
365                               tr("Mussa Save Error"),
366                               tr(e.what()),
367                               QMessageBox::Ok, 0, 0);
368   }
369       
370     }
371   }
372 }
373
374 void MussaWindow::saveAnalysisAs()
375 {
376   std::auto_ptr<QFileDialog> dialog(new QFileDialog(this));
377   dialog->setAcceptMode(QFileDialog::AcceptSave);
378   dialog->setFileMode(QFileDialog::AnyFile);
379   dialog->setDirectory(*default_dir);
380
381   QStringList fileNames;
382   if (not dialog->exec()) {
383     return;
384   }
385   fileNames = dialog->selectedFiles();
386    
387   if (fileNames.size() != 1) {
388     return;
389   }
390   try {
391     fs::path save_path(fileNames[0].toStdString(), fs::native);
392     // do you want to overwrite?
393     if (fs::exists(save_path) and 
394         QMessageBox::question(
395           this,
396           tr("Overwrite File? -- Mussa"),
397           tr("A file called %1 already exists"
398              "do you want to overwrite it?")
399              .arg(fileNames[0]),
400           tr("&Yes"), tr("&No"),
401           QString(), 0, 1)
402         ) {
403       return;
404     }
405     analysis->save(save_path);
406     fs::path normalized_path = (save_path / "..").normalize();   
407     default_dir->setPath(normalized_path.native_directory_string().c_str());
408   } catch (std::exception e) {
409     QMessageBox::critical(this, 
410                           tr("Mussa Save Error"),
411                           tr(e.what()),
412                           QMessageBox::Ok, 0, 0);
413   }
414 }
415
416 bool MussaWindow::isClearingAnalysisSafe()
417 {
418   if (analysis and not analysis->empty() and analysis->is_dirty()) {
419     switch (QMessageBox::question(
420       this,
421       tr("Save Unsaved Changes -- Mussa"),
422       tr("There are unsaved changes,\ndo you want to save?"),
423       tr("&Yes"), tr("&No"), tr("&Cancel"),
424       0, 2)) {
425     case 0:
426       // save
427       saveAnalysis();
428       break;
429     case 1:
430       // don't save
431       break;
432     case 2:
433       // don't replace 
434       return false;
435       break;
436     default:
437       // error
438       throw runtime_error("isClearingAnalysis QMesageBox failure");
439     }
440   } 
441   // if we're here we've been saved and can replace
442   return true;
443 }
444
445 void MussaWindow::editMotifs()
446 {
447   if (motif_editor != 0) {
448     motif_editor->hide();
449     delete motif_editor;
450   }
451   motif_editor = new MotifEditor(analysis);
452   connect(motif_editor, SIGNAL(changedMotifs()), 
453           this, SLOT(updateAnnotations()));
454   motif_editor->show();
455 }
456
457 void MussaWindow::loadMotifList()
458 {
459   QString caption("Mussa Load Motifs");
460   QString filter("Motif list(*.txt *.mtl)");
461   QString path = QFileDialog::getOpenFileName(this,
462                                               caption, 
463                                               default_dir->absolutePath(),
464                                               filter);
465   // user hit cancel?
466   if (path.isNull()) 
467     return;
468   // try to load safely
469   try {
470     fs::path converted_path(path.toStdString(), fs::native);
471     analysis->load_motifs(converted_path);
472     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
473     emit changedMotifs();
474   } catch (std::exception e) {
475     QString msg("Unable to load ");
476     msg += path;
477     msg += "\n";
478     msg += e.what();
479     QMessageBox::warning(this, caption, msg);
480   }
481 }
482
483 void MussaWindow::saveMotifList()
484 {
485   QString caption("Mussa Save Motifs");
486   QString filter("Motif list(*.txt *.mtl)");
487   QString path = QFileDialog::getSaveFileName(this,
488                                               caption, 
489                                               default_dir->absolutePath(),
490                                               filter);
491   // user hit cancel?
492   if (path.isNull()) 
493     return;
494   // try to load safely
495   try {
496     fs::path converted_path(path.toStdString(), fs::native);
497     if (fs::extension(converted_path).size() == 0) {
498       // no extension, so add one
499       fs::path base_path = converted_path.branch_path();
500       fs::path filename(converted_path.leaf() + ".mtl", fs::native);
501       converted_path = base_path / filename;
502     }
503     analysis->save_motifs(converted_path);
504     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
505   } catch (std::exception e) {
506     QString msg("Unable to save ");
507     msg += path;
508     msg += "\n";
509     msg += e.what();
510     QMessageBox::warning(this, caption, msg);
511   }}
512
513 void MussaWindow::loadMupa()
514 {
515   QString caption("Load a mussa parameter file");
516   QString filter("Mussa Parameters (*.mupa)");
517   QString mupa_path = QFileDialog::getOpenFileName(
518                          this,
519                          caption, 
520                          default_dir->absolutePath(),
521                          filter
522                       );
523   // user hit cancel?
524   if (mupa_path.isNull()) 
525     return;
526   // try to load safely
527   try {
528     // ideally we should open a new window if there's an analysis
529     // but this should work for the moment.
530     if (not isClearingAnalysisSafe()) return;
531
532     MussaRef m(new Mussa);
533     fs::path converted_path(mupa_path.toStdString(), fs::native);
534     connect(m.get(), SIGNAL(progress(const std::string&, int, int)),
535             this, SLOT(updateProgress(const std::string&, int, int)));
536     m->load_mupa_file(converted_path);
537     m->analyze();
538     setAnalysis(m);
539     updateTitle();
540     // grab the path ignoring the mupa file portion
541     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
542   } catch (mussa_load_error e) {
543     QString msg("Unable to load ");
544     msg += mupa_path;
545     msg += "\n";
546     msg += e.what();
547     QMessageBox::warning(this, "Load Parameter", msg);
548   }
549   assert (analysis != 0);
550 }
551
552 void MussaWindow::loadSavedAnalysis()
553 {
554   QString caption("Load a previously run analysis");
555   QString muway_dir = QFileDialog::getExistingDirectory(
556                         this,
557                         caption, 
558                         default_dir->absolutePath()
559                       );
560   // user hit cancel?
561   if (muway_dir.isNull()) 
562     return;
563   // try to safely load
564   try {
565     // ideally we should open a new window if there's an analysis
566     // but this should work for the moment.
567     if (not isClearingAnalysisSafe()) return;
568
569     MussaRef m(new Mussa);
570     fs::path converted_path(muway_dir.toStdString(), fs::native);
571     connect(m.get(), SIGNAL(progress(const std::string&, int, int)),
572             this, SLOT(updateProgress(const std::string&, int, int)));
573     m->load(converted_path);
574     // only switch mussas if we loaded without error
575     if (analysis->empty()) {
576       // our current window is empty so load and replace.
577       setAnalysis(m);
578       updateTitle();
579       default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
580     } else {
581       MussaWindow *win = new MussaWindow(m);
582       updateTitle();
583       win->default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
584       win->show();
585     }
586   } catch (boost::filesystem::filesystem_error e) {
587     QString msg("Unable to load ");
588     msg += muway_dir;
589     msg += "\n";
590     msg += e.what();
591     QMessageBox::warning(this, "Load Parameter", msg);    
592   } catch (mussa_load_error e) {
593     QString msg("Unable to load ");
594     msg += muway_dir;
595     msg += "\n";
596     msg += e.what();
597     QMessageBox::warning(this, "Load Parameter", msg);
598   }
599   assert (analysis != 0);
600 }
601
602 void MussaWindow::newMussaWindow()
603 {
604   MussaRef a(new Mussa);
605   MussaWindow *win = new MussaWindow(a);
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 string& 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 desc(description.c_str());
791       QString cancel("Cancel");
792       progress_dialog = new QProgressDialog(desc, 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 }