make mussawindow initialize icons
[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 static void init_resources() {
36   static bool resources_loaded = false;
37   if (not resources_loaded) {
38     Q_INIT_RESOURCE(icons);
39     resources_loaded = true;
40   }
41 }
42
43 MussaWindow::MussaWindow(MussaRef analysis_, QWidget *parent) :
44   QMainWindow(parent),
45   analysis(analysis_),
46   motif_editor(0),
47   setup_analysis_dialog(0),
48   browser(0),
49   mussaViewTB(0),
50   zoom(0),
51   threshold(0),
52   progress_dialog(0),
53   aboutAction(0),
54   closeAction(0),
55   createNewAnalysisAction(0),
56   createSubAnalysisAction(0),
57   editMotifsAction(0),
58   loadMotifListAction(0),
59   loadMupaAction(0),
60   loadSavedAnalysisAction(0),
61   mussaManualAssistantAction(0),
62   newMussaWindowAction(0),
63   saveMotifListAction(0),
64   showMussaViewToolbarAction(0),
65   toggleMotifsAction(0),
66   saveBrowserPixmapAction(0),
67   whatsThisAction(0),
68   viewMussaAlignmentAction(0),
69   manualAssistant(0)
70 {
71   init_resources();
72   setupWidgets();
73   setupActions();
74   setupAssistant();
75   setupMainMenu();
76   
77   setWindowIcon(QIcon(":/icons/mussa.png"));
78   default_dir.reset(new QDir(QDir::home().absolutePath()));
79
80   setCentralWidget(browser);
81   // well updatePosition isn't quite right as we really just need
82   // to call update()
83   connect(this, SIGNAL(changedAnnotations()), browser, SLOT(update()));
84   connect(this, SIGNAL(changedMotifs()), this, SLOT(updateAnnotations()));
85   connect(browser, SIGNAL(basepairsCopied(size_t)), 
86           this, SLOT(showBasePairsCopied(size_t)));
87   connect(zoom, SIGNAL(valueChanged(double)), 
88           browser, SLOT(setZoom(double)));
89   mussaViewTB->addWidget(zoom);
90
91   // threshold range is set in updateAnalysis  
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
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::setupWidgets()
284 {
285   setup_analysis_dialog = new MussaSetupDialog;
286   subanalysis_window.reset(new SubanalysisWindow(analysis));
287   browser = new SequenceBrowserWidget(default_dir);
288   mussaViewTB = new QToolBar("Path Views", this);
289   zoom = new ZoomWidget(mussaViewTB);
290   threshold = new ThresholdWidget(mussaViewTB);
291 }
292
293 void MussaWindow::setupAssistant()
294 {
295 #if defined(QT_QTASSISTANT_FOUND)
296   QStringList manualAssistantArgs;
297   manualAssistantArgs = QStringList();
298   manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp";
299   manualAssistant = new QAssistantClient("assistant", this);
300   manualAssistant->setArguments(manualAssistantArgs);
301   connect(manualAssistant, SIGNAL(error(QString)),
302           this, SLOT(assistantError(QString)));
303 #endif
304 }
305   
306 void MussaWindow::about()
307 {
308   QString msg;
309   msg += "Welcome to Multiple Species Sequence Analysis\n";
310   msg += "(c) 2005-2006 California Institute of Technology\n";
311   msg += "Diane Trout, Tristan De Buysscher, Brandon King\n";
312   msg += "Version: ";
313   msg += mussa_version;
314   msg += "\n";
315   msg += "OpenGL: ";
316   msg += (char *)glGetString(GL_VERSION);
317   msg += "\n";
318   QMessageBox::about(this, tr("About mussa"), msg);
319 }
320
321 void MussaWindow::clear()
322 {
323   aligned_windows.clear();
324   browser->clear();
325 }
326
327 void MussaWindow::createNewAnalysis()
328 {
329   try {
330     // ideally we should open a new window if there's an analysis
331     // but this should work for the moment.
332     if (not isClearingAnalysisSafe()) return;
333
334     if (setup_analysis_dialog->exec()) {
335       setAnalysis(setup_analysis_dialog->getMussa());
336     }
337   } catch(mussa_error e) {
338     QString msg(e.what());
339     QMessageBox::warning(this, tr("Create New Analysis"), msg);
340   }
341 }
342
343 void MussaWindow::createSubAnalysis()
344 {
345   list<SequenceLocation> result;
346   SequenceLocationModel& model = subanalysis_window->getModel();
347   browser->copySelectedTracksAsSeqLocation(result);
348   for(list<SequenceLocation>::iterator result_itor = result.begin();
349       result_itor != result.end();
350       ++result_itor)
351   {
352     model.push_back(*result_itor);
353   }
354
355   if (not subanalysis_window->isVisible()) {
356     subanalysis_window->show();
357   }
358 }
359
360 void MussaWindow::saveAnalysis()
361 {
362   // if we've got an analysis
363   if (analysis and not analysis->empty()) {
364     // if it doesn't have a name we need to pick one
365     if (analysis->get_analysis_path().empty()) {
366       saveAnalysisAs();
367     } else {     
368       // if we've got a name, has it changed any?
369       // this doesn't work when a sequence changes something (like its
370       // name)
371       //else if (analysis->is_dirty())
372       try { 
373         analysis->save();
374       } catch (std::exception e) {
375         QMessageBox::critical(this, 
376                               tr("Mussa Save Error"),
377                               tr(e.what()),
378                               QMessageBox::Ok, 0, 0);
379   }
380       
381     }
382   }
383 }
384
385 void MussaWindow::saveAnalysisAs()
386 {
387   std::auto_ptr<QFileDialog> dialog(new QFileDialog(this));
388   dialog->setAcceptMode(QFileDialog::AcceptSave);
389   dialog->setFileMode(QFileDialog::AnyFile);
390   dialog->setDirectory(*default_dir);
391
392   QStringList fileNames;
393   if (not dialog->exec()) {
394     return;
395   }
396   fileNames = dialog->selectedFiles();
397    
398   if (fileNames.size() != 1) {
399     return;
400   }
401   try {
402     fs::path save_path(fileNames[0].toStdString(), fs::native);
403     // do you want to overwrite?
404     if (fs::exists(save_path) and 
405         QMessageBox::question(
406           this,
407           tr("Overwrite File? -- Mussa"),
408           tr("A file called %1 already exists"
409              "do you want to overwrite it?")
410              .arg(fileNames[0]),
411           tr("&Yes"), tr("&No"),
412           QString(), 0, 1)
413         ) {
414       return;
415     }
416     analysis->save(save_path);
417     fs::path normalized_path = (save_path / "..").normalize();   
418     default_dir->setPath(normalized_path.native_directory_string().c_str());
419   } catch (std::exception e) {
420     QMessageBox::critical(this, 
421                           tr("Mussa Save Error"),
422                           tr(e.what()),
423                           QMessageBox::Ok, 0, 0);
424   }
425 }
426
427 bool MussaWindow::isClearingAnalysisSafe()
428 {
429   if (analysis and not analysis->empty() and analysis->is_dirty()) {
430     switch (QMessageBox::question(
431       this,
432       tr("Save Unsaved Changes -- Mussa"),
433       tr("There are unsaved changes,\ndo you want to save?"),
434       tr("&Yes"), tr("&No"), tr("&Cancel"),
435       0, 2)) {
436     case 0:
437       // save
438       saveAnalysis();
439       break;
440     case 1:
441       // don't save
442       break;
443     case 2:
444       // don't replace 
445       return false;
446       break;
447     default:
448       // error
449       throw runtime_error("isClearingAnalysis QMesageBox failure");
450     }
451   } 
452   // if we're here we've been saved and can replace
453   return true;
454 }
455
456 void MussaWindow::editMotifs()
457 {
458   if (motif_editor != 0) {
459     motif_editor->hide();
460     delete motif_editor;
461   }
462   motif_editor = new MotifEditor(analysis);
463   connect(motif_editor, SIGNAL(changedMotifs()), 
464           this, SLOT(updateAnnotations()));
465   motif_editor->show();
466 }
467
468 void MussaWindow::loadMotifList()
469 {
470   QString caption("Mussa Load Motifs");
471   QString filter("Motif list(*.txt *.mtl)");
472   QString path = QFileDialog::getOpenFileName(this,
473                                               caption, 
474                                               default_dir->absolutePath(),
475                                               filter);
476   // user hit cancel?
477   if (path.isNull()) 
478     return;
479   // try to load safely
480   try {
481     fs::path converted_path(path.toStdString(), fs::native);
482     analysis->load_motifs(converted_path);
483     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
484     emit changedMotifs();
485   } catch (std::exception e) {
486     QString msg("Unable to load ");
487     msg += path;
488     msg += "\n";
489     msg += e.what();
490     QMessageBox::warning(this, caption, msg);
491   }
492 }
493
494 void MussaWindow::saveMotifList()
495 {
496   QString caption("Mussa Save Motifs");
497   QString filter("Motif list(*.txt *.mtl)");
498   QString path = QFileDialog::getSaveFileName(this,
499                                               caption, 
500                                               default_dir->absolutePath(),
501                                               filter);
502   // user hit cancel?
503   if (path.isNull()) 
504     return;
505   // try to load safely
506   try {
507     fs::path converted_path(path.toStdString(), fs::native);
508     if (fs::extension(converted_path).size() == 0) {
509       // no extension, so add one
510       fs::path base_path = converted_path.branch_path();
511       fs::path filename(converted_path.leaf() + ".mtl", fs::native);
512       converted_path = base_path / filename;
513     }
514     analysis->save_motifs(converted_path);
515     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
516   } catch (std::exception e) {
517     QString msg("Unable to save ");
518     msg += path;
519     msg += "\n";
520     msg += e.what();
521     QMessageBox::warning(this, caption, msg);
522   }}
523
524 void MussaWindow::loadMupa()
525 {
526   QString caption("Load a mussa parameter file");
527   QString filter("Mussa Parameters (*.mupa)");
528   QString mupa_path = QFileDialog::getOpenFileName(
529                          this,
530                          caption, 
531                          default_dir->absolutePath(),
532                          filter
533                       );
534   // user hit cancel?
535   if (mupa_path.isNull()) 
536     return;
537   // try to load safely
538   try {
539     // ideally we should open a new window if there's an analysis
540     // but this should work for the moment.
541     if (not isClearingAnalysisSafe()) return;
542
543     MussaRef m = Mussa::init();
544     fs::path converted_path(mupa_path.toStdString(), fs::native);
545     connect(m.get(), SIGNAL(progress(const QString&, int, int)),
546             this, SLOT(updateProgress(const QString&, int, int)));
547     m->load_mupa_file(converted_path);
548     m->analyze();
549     setAnalysis(m);
550     updateTitle();
551     // grab the path ignoring the mupa file portion
552     default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
553   } catch (mussa_load_error e) {
554     QString msg("Unable to load ");
555     msg += mupa_path;
556     msg += "\n";
557     msg += e.what();
558     QMessageBox::warning(this, "Load Parameter", msg);
559   }
560   assert (analysis != 0);
561 }
562
563 void MussaWindow::loadSavedAnalysis()
564 {
565   QString caption("Load a previously run analysis");
566   QString muway_dir = QFileDialog::getExistingDirectory(
567                         this,
568                         caption, 
569                         default_dir->absolutePath()
570                       );
571   // user hit cancel?
572   if (muway_dir.isNull()) 
573     return;
574   // try to safely load
575   try {
576     // ideally we should open a new window if there's an analysis
577     // but this should work for the moment.
578     if (not isClearingAnalysisSafe()) return;
579
580     MussaRef m = Mussa::init();
581     fs::path converted_path(muway_dir.toStdString(), fs::native);
582     connect(m.get(), SIGNAL(progress(const QString&, int, int)),
583             this, SLOT(updateProgress(const QString&, int, int)));
584     m->load(converted_path);
585     // only switch mussas if we loaded without error
586     if (analysis->empty()) {
587       // our current window is empty so load and replace.
588       setAnalysis(m);
589       updateTitle();
590       default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
591     } else {
592       MussaWindow *win = new MussaWindow(m);
593       updateTitle();
594       win->default_dir->setPath(converted_path.branch_path().native_directory_string().c_str());
595       win->show();
596     }
597   } catch (boost::filesystem::filesystem_error e) {
598     QString msg("Unable to load ");
599     msg += muway_dir;
600     msg += "\n";
601     msg += e.what();
602     QMessageBox::warning(this, "Load Parameter", msg);    
603   } catch (mussa_load_error e) {
604     QString msg("Unable to load ");
605     msg += muway_dir;
606     msg += "\n";
607     msg += e.what();
608     QMessageBox::warning(this, "Load Parameter", msg);
609   }
610   assert (analysis != 0);
611 }
612
613 void MussaWindow::newMussaWindow()
614 {
615   MussaWindow *win = new MussaWindow(Mussa::init());
616   win->default_dir = default_dir;
617   win->show();
618 }
619
620 void MussaWindow::setSoftThreshold(int value)
621 {
622   if (analysis->get_soft_threshold() != value) {
623     threshold->setEnabled( false );
624     analysis->set_soft_threshold(value);
625     analysis->nway();
626     updateLinks();
627     update();
628     threshold->setEnabled( true );
629   }
630 }
631
632 void MussaWindow::showMussaToolbar()
633 {
634   if (mussaViewTB->isVisible())
635     mussaViewTB->hide();
636   else
637     mussaViewTB->show();
638 }
639
640 void MussaWindow::showBasePairsCopied(size_t bp_copied)
641 {
642   QString msg("Copied ");
643   QString num;
644   num.setNum(bp_copied);
645   msg += num + " base pairs";
646   statusBar()->showMessage(msg, 5000);
647 }
648
649
650 void MussaWindow::toggleMotifs()
651 {
652   NotImplementedBox();
653 }
654
655 void MussaWindow::showManual()
656 {
657 #if defined(QT_QTASSISTANT_FOUND)
658   if (manualAssistant) { 
659     manualAssistant->openAssistant();
660   } else {
661     QMessageBox::warning(this,
662                          tr("Mussa Help Error"),
663                          tr("QtAssistant not setup correctly"),
664                          QMessageBox::Ok,
665                          QMessageBox::NoButton,
666                          QMessageBox::NoButton);
667   }
668 #else
669   QUrl manual_url("http://woldlab.caltech.edu/~king/mussagl_manual/");
670   if (not QDesktopServices::openUrl(manual_url)) {
671     QMessageBox::warning(this,
672                          tr("Mussa Help Error"),
673                          tr("Unable to launch webbrowser"),
674                          QMessageBox::Ok,
675                          QMessageBox::NoButton,
676                          QMessageBox::NoButton);
677   }
678 #endif //QT_QTASSISTANT_FOUND
679 }
680
681 void MussaWindow::assistantError(QString message)
682 {
683   //std::cout << "QAssistantError: " << message.toStdString() << "\n";
684   QMessageBox::warning ( this, "Warning: Mussagl Manual", message, 
685                          QMessageBox::Ok, 
686                          QMessageBox::NoButton);
687 }
688
689 void MussaWindow::NotImplementedBox()
690 {
691   QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
692 }      
693
694 void MussaWindow::viewMussaAlignment()
695 {
696   const set<int>& selected_paths = browser->selectedPaths();
697   if (selected_paths.size() == 0 ) {
698     QMessageBox::warning(this, 
699                          QObject::tr("mussa"),
700                          QObject::tr("you should probably select some paths "
701                                      "first"));
702   } else {
703     MussaAlignedWindowRef ma_win( 
704       new MussaAlignedWindow(analysis, default_dir, selected_paths, subanalysis_window)
705     );
706
707     aligned_windows.push_back(ma_win);
708     connect(this, SIGNAL(changedAnnotations()), 
709             aligned_windows.back().get(), SLOT(update()));
710     aligned_windows.back()->show();
711   }
712 }
713                         
714 void MussaWindow::updateAnalysis()
715 {
716   const Mussa::vector_sequence_type& seqs = analysis->sequences();
717   browser->setSequences(seqs, analysis->colorMapper());
718   assert(browser->sequences().size() == analysis->size());
719
720   // setRange eventually emits something that causes updateLinks to be called
721   // but it's possible for us to not have had a chance to set out sequences
722   // yet.
723   threshold->setRange(analysis->get_threshold(),analysis->get_window());
724   updateLinks();
725   browser->zoomOut();
726   zoom->setValue(browser->zoom());
727 }
728
729 void MussaWindow::updateAnnotations()
730 {
731   // motifs were changed in the sequences by 
732   // Mussa::update_sequences_motifs
733   emit changedAnnotations();
734   browser->update();
735 }
736
737 void MussaWindow::updateLinks()
738 {
739   if(browser->sequences().size() == 0) {
740     // we don't have any sequences load so we have no business setting links
741     return;
742   }
743
744   browser->clear_links();
745   bool reversed = false;
746   const NwayPaths& nway = analysis->paths();
747
748   typedef list<ConservedPath> conserved_paths;
749   typedef conserved_paths::const_iterator const_conserved_paths_itor;
750   for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
751       path_itor != nway.refined_pathz.end();
752       ++path_itor)
753   {
754     // since we were drawing to the start of a window, and opengl lines
755     // are centered around the two connecting points our lines were slightly
756     // offset. the idea of window_offset is to adjust them to the
757     // right for forward compliment or left for reverse compliment
758     // FIXME: figure out how to unit test these computations
759     //GLfloat window_offset = (path_itor->window_size)/2.0;
760
761     size_t track_len = path_itor->track_indexes.size();
762     vector<int> normalized_path;
763     normalized_path.reserve(track_len);
764     vector<bool> rc_flags(false, track_len);
765     for (size_t track_i=0; track_i != track_len; ++track_i)
766     {
767       int x = path_itor->track_indexes[track_i];
768       // at some point when we modify the pathz data structure to keep
769       // track of the score we can put grab the depth here.
770       //
771       // are we reverse complimented?
772       if ( x>=0) {
773         reversed = false;
774       } else {
775         reversed = true;
776         // make positive 
777         x = -x;
778       }
779       normalized_path.push_back(x);
780       rc_flags.push_back(reversed);
781     }
782     browser->link(normalized_path, rc_flags, path_itor->window_size);
783   }
784   browser->update();
785 }
786
787 void 
788 MussaWindow::updateProgress(const QString& description, int current, int max)
789 {  
790   // if we're done  
791   if (current == max) {
792     if (progress_dialog != 0) {
793       progress_dialog->hide();
794       delete progress_dialog;
795       progress_dialog = 0;
796     }
797   } else {
798     // if we're starting, create the dialog
799     if (progress_dialog == 0) {
800       QString cancel("Cancel");
801       progress_dialog = new QProgressDialog(description, cancel, current, max, this);
802       progress_dialog->show();
803     } else {
804       // just update the dialog
805       progress_dialog->setValue(current);
806     }
807   }
808   qApp->processEvents();
809 }
810
811 void MussaWindow::updateAnalysisModified(bool is_modified)
812 {
813   setWindowModified(is_modified);
814 }
815
816 void MussaWindow::updateTitle()
817 {
818   if (analysis) {
819     QString title(analysis->get_title().c_str());
820     title += "[*]";
821     setWindowTitle(title);
822   }
823 }