Fix initialization of QtAssistant
[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_QTASSISTANT_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("Mussa Load Motifs");
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, caption, msg);
451   }
452 }
453
454 void MussaWindow::saveMotifList()
455 {
456   QString caption("Mussa Save Motifs");
457   QString filter("Motif list(*.txt *.mtl)");
458   QDir default_qdir(default_dir.native_directory_string().c_str());
459   QString path = QFileDialog::getSaveFileName(this,
460                                               caption, 
461                                               default_qdir.absolutePath(),
462                                               filter);
463   // user hit cancel?
464   if (path.isNull()) 
465     return;
466   // try to load safely
467   try {
468     fs::path converted_path(path.toStdString(), fs::native);
469     analysis->save_motifs(converted_path);
470     default_dir = converted_path.branch_path();
471   } catch (runtime_error e) {
472     QString msg("Unable to save ");
473     msg += path;
474     msg += "\n";
475     msg += e.what();
476     QMessageBox::warning(this, caption, msg);
477   }}
478
479 void MussaWindow::loadMupa()
480 {
481   QString caption("Load a mussa parameter file");
482   QString filter("Mussa Parameters (*.mupa)");
483   QDir default_qdir(QDir(default_dir.native_directory_string().c_str()));
484
485   QString mupa_path = QFileDialog::getOpenFileName(
486                          this,
487                          caption, 
488                          default_qdir.absolutePath(),
489                          filter
490                       );
491   // user hit cancel?
492   if (mupa_path.isNull()) 
493     return;
494   // try to load safely
495   try {
496     // ideally we should open a new window if there's an analysis
497     // but this should work for the moment.
498     if (not isClearingAnalysisSafe()) return;
499
500     MussaRef m(new Mussa);
501     fs::path converted_path(mupa_path.toStdString(), fs::native);
502     connect(m.get(), SIGNAL(progress(const std::string&, int, int)),
503             this, SLOT(updateProgress(const std::string&, int, int)));
504     m->load_mupa_file(converted_path);
505     m->analyze();
506     setAnalysis(m);
507     updateTitle();
508     // grab the path ignoring the mupa file portion
509     default_dir = converted_path.branch_path();
510   } catch (mussa_load_error e) {
511     QString msg("Unable to load ");
512     msg += mupa_path;
513     msg += "\n";
514     msg += e.what();
515     QMessageBox::warning(this, "Load Parameter", msg);
516   }
517   assert (analysis != 0);
518 }
519
520 void MussaWindow::loadSavedAnalysis()
521 {
522   QDir default_qdir(QDir(default_dir.native_directory_string().c_str()));
523   QString caption("Load a previously run analysis");
524   QString muway_dir = QFileDialog::getExistingDirectory(
525                         this,
526                         caption, 
527                         default_qdir.absolutePath()
528                       );
529   // user hit cancel?
530   if (muway_dir.isNull()) 
531     return;
532   // try to safely load
533   try {
534     // ideally we should open a new window if there's an analysis
535     // but this should work for the moment.
536     if (not isClearingAnalysisSafe()) return;
537
538     MussaRef m(new Mussa);
539     fs::path converted_path(muway_dir.toStdString(), fs::native);
540     connect(m.get(), SIGNAL(progress(const std::string&, int, int)),
541             this, SLOT(updateProgress(const std::string&, int, int)));
542     m->load(converted_path);
543     // only switch mussas if we loaded without error
544     if (analysis->empty()) {
545       // our current window is empty so load and replace.
546       setAnalysis(m);
547       updateTitle();
548       default_dir = converted_path.branch_path();
549     } else {
550       MussaWindow *win = new MussaWindow(m);
551       updateTitle();
552       win->default_dir = converted_path.branch_path();
553       win->show();
554     }
555   } catch (mussa_load_error e) {
556     QString msg("Unable to load ");
557     msg += muway_dir;
558     msg += "\n";
559     msg += e.what();
560     QMessageBox::warning(this, "Load Parameter", msg);
561   }
562   assert (analysis != 0);
563 }
564
565 void MussaWindow::newMussaWindow()
566 {
567   MussaRef a(new Mussa);
568   MussaWindow *win = new MussaWindow(a);
569   win->default_dir = default_dir;
570   win->show();
571 }
572
573 void MussaWindow::setSoftThreshold(int threshold)
574 {
575   if (analysis->get_soft_threshold() != threshold) {
576     analysis->set_soft_threshold(threshold);
577     analysis->nway();
578     updateLinks();
579     update();
580   }
581 }
582
583 void MussaWindow::showMussaToolbar()
584 {
585   if (mussaViewTB->isVisible())
586     mussaViewTB->hide();
587   else
588     mussaViewTB->show();
589 }
590
591 void MussaWindow::toggleMotifs()
592 {
593   NotImplementedBox();
594 }
595
596 void MussaWindow::showManual()
597 {
598 #if defined(QT_QTASSISTANT_FOUND)
599   if (manualAssistant) { 
600     manualAssistant->openAssistant();
601   } else {
602     QMessageBox::warning(this,
603                          tr("Mussa Help Error"),
604                          tr("QtAssistant not setup correctly"),
605                          QMessageBox::Ok,
606                          QMessageBox::NoButton,
607                          QMessageBox::NoButton);
608   }
609 #else
610   try {
611     boost::python::object webopen = get_py()["webbrowser.open"];
612     webopen("http://woldlab.caltech.edu/~king/mussagl_manual/");
613   } catch( boost::python::error_already_set ) {
614     PyErr_Print();
615     QMessageBox::warning(this,
616                          tr("Mussa Help Error"),
617                          tr("Unable to launch webbrowser"),
618                          QMessageBox::Ok,
619                          QMessageBox::NoButton,
620                          QMessageBox::NoButton);
621   }
622 #endif //QT_QTASSISTANT_FOUND
623 }
624
625 void MussaWindow::assistantError(QString message)
626 {
627   //std::cout << "QAssistantError: " << message.toStdString() << "\n";
628   QMessageBox::warning ( this, "Warning: Mussagl Manual", message, 
629                          QMessageBox::Ok, 
630                          QMessageBox::NoButton);
631 }
632
633 void MussaWindow::NotImplementedBox()
634 {
635   QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet"));
636 }      
637
638 void MussaWindow::viewMussaAlignment()
639 {
640   const set<int>& selected_paths = browser->selectedPaths();
641   if (selected_paths.size() == 0 ) {
642     QMessageBox::warning(this, 
643                          QObject::tr("mussa"),
644                          QObject::tr("you should probably select some paths "
645                                      "first"));
646   } else {
647     MussaAlignedWindowRef ma_win( 
648       new MussaAlignedWindow(analysis, selected_paths, subanalysis_window)
649     );
650
651     aligned_windows.push_back(ma_win);
652     connect(this, SIGNAL(changedAnnotations()), 
653             aligned_windows.back().get(), SLOT(update()));
654     aligned_windows.back()->show();
655   }
656 }
657                         
658 void MussaWindow::updateAnalysis()
659 {
660   const Mussa::vector_sequence_type& seqs = analysis->sequences();
661   browser->setSequences(seqs, analysis->colorMapper());
662   assert(browser->sequences().size() == analysis->size());
663
664   // setRange eventually emits something that causes updateLinks to be called
665   // but it's possible for us to not have had a chance to set out sequences
666   // yet.
667   threshold->setRange(analysis->get_threshold(),analysis->get_window());
668   updateLinks();
669   browser->zoomOut();
670   zoom->setValue(browser->zoom());
671 }
672
673 void MussaWindow::updateAnnotations()
674 {
675   // motifs were changed in the sequences by 
676   // Mussa::update_sequences_motifs
677   emit changedAnnotations();
678   browser->update();
679 }
680
681 void MussaWindow::updateLinks()
682 {
683   if(browser->sequences().size() == 0) {
684     // we don't have any sequences load so we have no business setting links
685     return;
686   }
687
688   browser->clear_links();
689   bool reversed = false;
690   const NwayPaths& nway = analysis->paths();
691
692   typedef list<ConservedPath> conserved_paths;
693   typedef conserved_paths::const_iterator const_conserved_paths_itor;
694   for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin();
695       path_itor != nway.refined_pathz.end();
696       ++path_itor)
697   {
698     // since we were drawing to the start of a window, and opengl lines
699     // are centered around the two connecting points our lines were slightly
700     // offset. the idea of window_offset is to adjust them to the
701     // right for forward compliment or left for reverse compliment
702     // FIXME: figure out how to unit test these computations
703     //GLfloat window_offset = (path_itor->window_size)/2.0;
704
705     size_t track_len = path_itor->track_indexes.size();
706     vector<int> normalized_path;
707     normalized_path.reserve(track_len);
708     vector<bool> rc_flags(false, track_len);
709     for (size_t track_i=0; track_i != track_len; ++track_i)
710     {
711       int x = path_itor->track_indexes[track_i];
712       // at some point when we modify the pathz data structure to keep
713       // track of the score we can put grab the depth here.
714       //
715       // are we reverse complimented?
716       if ( x>=0) {
717         reversed = false;
718       } else {
719         reversed = true;
720         // make positive 
721         x = -x;
722       }
723       normalized_path.push_back(x);
724       rc_flags.push_back(reversed);
725     }
726     browser->link(normalized_path, rc_flags, path_itor->window_size);
727   }
728   browser->update();
729 }
730
731 void 
732 MussaWindow::updateProgress(const string& description, int current, int max)
733 {  
734   // if we're done  
735   if (current == max) {
736     if (progress_dialog != 0) {
737       progress_dialog->hide();
738       delete progress_dialog;
739       progress_dialog = 0;
740     }
741   } else {
742     // if we're starting, create the dialog
743     if (progress_dialog == 0) {
744       QString desc(description.c_str());
745       QString cancel("Cancel");
746       progress_dialog = new QProgressDialog(desc, cancel, current, max, this);
747       progress_dialog->show();
748     } else {
749       // just update the dialog
750       progress_dialog->setValue(current);
751     }
752   }
753   qApp->processEvents();
754 }
755
756 void MussaWindow::updateTitle()
757 {
758   if (analysis) {
759     setWindowTitle(analysis->get_title().c_str());
760   }
761 }