trigger motif update from loadMotifList
[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 QT_QTASSISTANT_FOUND
580   manualAssistant->openAssistant();
581 #else
582   try {
583     boost::python::object webopen = get_py()["webbrowser.open"];
584     webopen("http://woldlab.caltech.edu/~king/mussagl_manual/");
585   } catch( boost::python::error_already_set ) {
586     PyErr_Print();
587   }
588 #endif //QT_QTASSISTANT_FOUND
589 }
590
591 void MussaWindow::assistantError(QString message)
592 {
593   //std::cout << "QAssistantError: " << message.toStdString() << "\n";
594   QMessageBox::warning ( this, "Warning: Mussagl Manual", message, 
595                          QMessageBox::Ok, 
596                          QMessageBox::NoButton);
597 }
598
599 void MussaWindow::NotImplementedBox()
600 {
601   QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
602 }      
603
604 void MussaWindow::viewMussaAlignment()
605 {
606   const set<int>& selected_paths = browser->selectedPaths();
607   if (selected_paths.size() == 0 ) {
608     QMessageBox::warning(this, 
609                          QObject::tr("mussa"),
610                          QObject::tr("you should probably select some paths "
611                                      "first"));
612   } else {
613     MussaAlignedWindowRef ma_win( 
614       new MussaAlignedWindow(analysis, selected_paths, subanalysis_window)
615     );
616
617     aligned_windows.push_back(ma_win);
618     connect(this, SIGNAL(changedAnnotations()), 
619             aligned_windows.back().get(), SLOT(update()));
620     aligned_windows.back()->show();
621   }
622 }
623                         
624 void MussaWindow::updateAnalysis()
625 {
626   const Mussa::vector_sequence_type& seqs = analysis->sequences();
627   browser->setSequences(seqs, analysis->colorMapper());
628   assert(browser->sequences().size() == analysis->size());
629
630   // setRange eventually emits something that causes updateLinks to be called
631   // but it's possible for us to not have had a chance to set out sequences
632   // yet.
633   threshold->setRange(analysis->get_threshold(),analysis->get_window());
634   updateLinks();
635   browser->zoomOut();
636   zoom->setValue(browser->zoom());
637 }
638
639 void MussaWindow::updateAnnotations()
640 {
641   // motifs were changed in the sequences by 
642   // Mussa::update_sequences_motifs
643   emit changedAnnotations();
644   browser->update();
645 }
646
647 void MussaWindow::updateLinks()
648 {
649   if(browser->sequences().size() == 0) {
650     // we don't have any sequences load so we have no business setting links
651     return;
652   }
653
654   browser->clear_links();
655   bool reversed = false;
656   const NwayPaths& nway = analysis->paths();
657
658   typedef list<ConservedPath> conserved_paths;
659   typedef conserved_paths::const_iterator const_conserved_paths_itor;
660   for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
661       path_itor != nway.refined_pathz.end();
662       ++path_itor)
663   {
664     // since we were drawing to the start of a window, and opengl lines
665     // are centered around the two connecting points our lines were slightly
666     // offset. the idea of window_offset is to adjust them to the
667     // right for forward compliment or left for reverse compliment
668     // FIXME: figure out how to unit test these computations
669     //GLfloat window_offset = (path_itor->window_size)/2.0;
670
671     size_t track_len = path_itor->track_indexes.size();
672     vector<int> normalized_path;
673     normalized_path.reserve(track_len);
674     vector<bool> rc_flags(false, track_len);
675     for (size_t track_i=0; track_i != track_len; ++track_i)
676     {
677       int x = path_itor->track_indexes[track_i];
678       // at some point when we modify the pathz data structure to keep
679       // track of the score we can put grab the depth here.
680       //
681       // are we reverse complimented?
682       if ( x>=0) {
683         reversed = false;
684       } else {
685         reversed = true;
686         // make positive 
687         x = -x;
688       }
689       normalized_path.push_back(x);
690       rc_flags.push_back(reversed);
691     }
692     browser->link(normalized_path, rc_flags, path_itor->window_size);
693   }
694   browser->update();
695 }
696
697 void 
698 MussaWindow::updateProgress(const string& description, int current, int max)
699 {  
700   // if we're done  
701   if (current == max) {
702     if (progress_dialog != 0) {
703       progress_dialog->hide();
704       delete progress_dialog;
705       progress_dialog = 0;
706     }
707   } else {
708     // if we're starting, create the dialog
709     if (progress_dialog == 0) {
710       QString desc(description.c_str());
711       QString cancel("Cancel");
712       progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
713       progress_dialog->show();
714     } else {
715       // just update the dialog
716       progress_dialog->setValue(current);
717     }
718   }
719   qApp->processEvents();
720 }
721
722 void MussaWindow::updateTitle()
723 {
724   if (analysis) {
725     setWindowTitle(analysis->get_title().c_str());
726   }
727 }