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