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