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