X-Git-Url: http://woldlab.caltech.edu/gitweb/?a=blobdiff_plain;f=qui%2FMussaWindow.cpp;h=75da849d591416a25371c980e9454b8c46e737af;hb=15e1618344491d9860923656fc16be15532223a6;hp=b84da1df21b0225fb9434f45842e94cfeb890e61;hpb=54c537d0b50d417162103b7c7b3130e2ab1545b8;p=mussa.git diff --git a/qui/MussaWindow.cpp b/qui/MussaWindow.cpp index b84da1d..75da849 100644 --- a/qui/MussaWindow.cpp +++ b/qui/MussaWindow.cpp @@ -1,82 +1,140 @@ +#include "qui/MussaWindow.hpp" +#include "mussa_exceptions.hpp" +#include "version.hpp" + #include +#include +#include +#include +#include #include #include #include #include +#include #include #include +#include #include #include #include +#include +#include +#include #include -#include "qui/MussaWindow.hpp" -#include "qui/mussa_setup_dialog/MussaSetupDialog.hpp" -#include "mussa_exceptions.hpp" - +#include +#include #include +#include +#include +#include +namespace fs = boost::filesystem; +#include + using namespace std; -MussaWindow::MussaWindow(Mussa *analysis_, QWidget *parent) : +static void init_resources() { + static bool resources_loaded = false; + if (not resources_loaded) { + Q_INIT_RESOURCE(icons); + resources_loaded = true; + } +} + +MussaWindow::MussaWindow(MussaRef analysis_, QWidget *parent) : QMainWindow(parent), analysis(analysis_), motif_editor(0), - browser(this), - mussaViewTB("Path Views"), - zoomBox(), - threshold(), - zoomLabel(), - closeAction(0) // initialize one of the pointers to null as a saftey flag + setup_analysis_dialog(0), + browser(0), + mussaViewTB(0), + zoom(0), + threshold(0), + progress_dialog(0), + aboutAction(0), + closeAction(0), + createNewAnalysisAction(0), + createSubAnalysisAction(0), + editMotifsAction(0), + loadMotifListAction(0), + loadMupaAction(0), + loadSavedAnalysisAction(0), + mussaManualAssistantAction(0), + newMussaWindowAction(0), + saveMotifListAction(0), + showMussaViewToolbarAction(0), + toggleMotifsAction(0), + saveBrowserPixmapAction(0), + whatsThisAction(0), + viewMussaAlignmentAction(0), + assistantProcess(0) { + init_resources(); + + default_dir.reset(new QDir(QDir::home().absolutePath())); + + setupWidgets(); setupActions(); + setupAssistant(); setupMainMenu(); + + setWindowIcon(QIcon(":/icons/mussa.png")); - //This next setWhatsThis function prevents - // a segfault when using WhatsThis feature with - // opengl widget. - //scene->setWhatsThis(tr("Mussa in OpenGL!")); - setCentralWidget(&browser); + setCentralWidget(browser); // well updatePosition isn't quite right as we really just need // to call update() - connect(this, SIGNAL(changedAnnotations()), &browser, SLOT(update())); - - mussaViewTB.addAction(toggleMotifsAction); + connect(this, SIGNAL(changedAnnotations()), browser, SLOT(update())); + connect(this, SIGNAL(changedMotifs()), this, SLOT(updateAnnotations())); + connect(browser, SIGNAL(basepairsCopied(size_t)), + this, SLOT(showBasePairsCopied(size_t))); + connect(zoom, SIGNAL(valueChanged(double)), + browser, SLOT(setZoom(double))); + mussaViewTB->addWidget(zoom); - zoomLabel.setPixmap(QIcon("icons/viewmag.png").pixmap(16, 16)); - zoomLabel.setToolTip(tr("Zoom")); - zoomLabel.setWhatsThis(tr("Zoom magnification factor")); - mussaViewTB.addWidget(&zoomLabel); - - zoomBox.setToolTip(tr("Zoom")); - zoomBox.setWhatsThis(tr("Zoom magnification factor")); - zoomBox.setRange(1,10000000); - mussaViewTB.addWidget(&zoomBox); - connect(&zoomBox, SIGNAL(valueChanged(int)), - &browser, SLOT(setZoom(int))); - - threshold.setRange(10,30); - threshold.setThreshold(20); - //scene->setClipPlane(20); - // FIXME: for when we get the paths drawn at the appropriate depth - //connect(&threshold, SIGNAL(thresholdChanged(int)), - // this, SLOT(setClipPlane(int))); - connect(&threshold, SIGNAL(thresholdChanged(int)), + // Mouse Wheel triggered zooming + connect(browser, SIGNAL(mouseWheelZoom(double)), + zoom, SLOT(setValue(double))); + + // threshold range is set in updateAnalysis + connect(threshold, SIGNAL(thresholdChanged(int)), this, SLOT(setSoftThreshold(int))); - mussaViewTB.addWidget(&threshold); + mussaViewTB->addWidget(threshold); - addToolBar(&mussaViewTB); + addToolBar(mussaViewTB); statusBar()->showMessage("Welcome to mussa", 2000); + + // FIXME: we should start refactoring the connect call to updateAnalysis or something + if (analysis) { + connect(analysis.get(), SIGNAL(progress(const QString&, int, int)), + this, SLOT(updateProgress(const QString&, int, int))); + connect(analysis.get(), SIGNAL(isModified(bool)), + this, SLOT(updateAnalysisModified(bool))); + } + updateTitle(); updateAnalysis(); } -void MussaWindow::setAnalysis(Mussa *new_analysis) +void MussaWindow::setAnalysis(MussaRef new_analysis) { if (new_analysis != 0) { // only switch mussas if we loaded without error - delete analysis; - analysis = new_analysis; + clear(); + //std::cout << "analysis soft: " << analysis->get_soft_threshold() + // << " new analysis soft: " << new_analysis->get_soft_threshold() + // << "\n"; + analysis.swap(new_analysis); + //std::cout << "after swap soft thres: " << analysis->get_soft_threshold() + // << "\n"; + threshold->disconnect(this); + threshold->reset(analysis->get_threshold(), + analysis->get_window(), + analysis->get_soft_threshold()); + connect(threshold, SIGNAL(thresholdChanged(int)), + this, SLOT(setSoftThreshold(int))); + updateTitle(); updateAnalysis(); } } @@ -89,45 +147,69 @@ void MussaWindow::setupActions() // the ever popular about box aboutAction = new QAction(tr("&About"), this); connect(aboutAction, SIGNAL(triggered()), this, SLOT(about())); - aboutAction->setIcon(QIcon("icons/info.png")); + aboutAction->setIcon(QIcon(":/icons/info.png")); // add exit closeAction = new QAction(tr("&Close"), this); closeAction->setStatusTip(tr("Close this window")); connect(closeAction, SIGNAL(triggered()), this, SLOT(close())); - closeAction->setIcon(QIcon("icons/exit.png")); + closeAction->setIcon(QIcon(":/icons/exit.png")); - createNewAnalysisAction = new QAction(tr("Define Analysis"), this); + createNewAnalysisAction = new QAction(tr("Create &New Analysis"), this); connect(createNewAnalysisAction, SIGNAL(triggered()), this, SLOT(createNewAnalysis())); - createNewAnalysisAction->setIcon(QIcon("icons/filenew.png")); + createNewAnalysisAction->setIcon(QIcon(":/icons/filenew.png")); + createNewAnalysisAction->setShortcut(Qt::CTRL | Qt::Key_N); - createSubAnalysisAction = new QAction(tr("Define SubAnalysis"), this); + createSubAnalysisAction = new QAction(tr("Add to Subanalysis"), this); connect(createSubAnalysisAction, SIGNAL(triggered()), this, SLOT(createSubAnalysis())); + saveAnalysisAction = new QAction(tr("&Save Analysis"), this); + connect(saveAnalysisAction, SIGNAL(triggered()), + this, SLOT(saveAnalysis())); + saveAnalysisAction->setIcon(QIcon(":/icons/filesave.png")); + saveAnalysisAction->setShortcut(Qt::CTRL | Qt::Key_S); + + saveAnalysisAsAction = new QAction(tr("Save Analysis &As"), this); + connect(saveAnalysisAsAction, SIGNAL(triggered()), + this, SLOT(saveAnalysisAs())); + saveAnalysisAsAction->setIcon(QIcon(":/icons/filesave.png")); + editMotifsAction = new QAction(tr("Edit Motifs"), this);; connect(editMotifsAction, SIGNAL(triggered()), this, SLOT(editMotifs())); - loadMotifListAction = new QAction(tr("Load Motif List"), this); + loadMotifListAction = new QAction(tr("Open Motif List"), this); connect(loadMotifListAction, SIGNAL(triggered()), this, SLOT(loadMotifList())); - loadMotifListAction->setIcon(QIcon("icons/fileopen.png")); + loadMotifListAction->setIcon(QIcon(":/icons/fileopen.png")); - loadMupaAction = new QAction(tr("Load Mussa Parameters"), this); + loadMupaAction = new QAction(tr("Create Analysis from File"), this); connect(loadMupaAction, SIGNAL(triggered()), this, SLOT(loadMupa())); - loadMupaAction->setIcon(QIcon("icons/fileopen.png")); + loadMupaAction->setIcon(QIcon(":/icons/fileopen.png")); - loadSavedAnalysisAction = new QAction(tr("Load &Analysis"), this); + loadSavedAnalysisAction = new QAction(tr("&Open Existing &Analysis"), this); connect(loadSavedAnalysisAction, SIGNAL(triggered()), this, SLOT(loadSavedAnalysis())); - loadSavedAnalysisAction->setIcon(QIcon("icons/fileopen.png")); + loadSavedAnalysisAction->setIcon(QIcon(":/icons/fileopen.png")); + loadSavedAnalysisAction->setShortcut(Qt::CTRL | Qt::Key_O); + + mussaManualAssistantAction = new QAction(tr("Mussagl Manual..."), this); + mussaManualAssistantAction->setIcon(QIcon(":/icons/contents.png")); + connect(mussaManualAssistantAction, SIGNAL(triggered()), + this, SLOT(showManual())); + + newMussaWindowAction = new QAction(tr("&New Mussa Window"), this); + newMussaWindowAction->setStatusTip("open another mussa window to allow comparing results"); + connect(newMussaWindowAction, SIGNAL(triggered()), + this, SLOT(newMussaWindow())); + newMussaWindowAction->setIcon(QIcon(":/icons/window_new.png")); saveMotifListAction = new QAction(tr("Save Motifs"), this); connect(saveMotifListAction, SIGNAL(triggered()), this, SLOT(saveMotifList())); - saveMotifListAction->setIcon(QIcon("icons/filesave.png")); + saveMotifListAction->setIcon(QIcon(":/icons/filesave.png")); showMussaViewToolbarAction = new QAction(tr("Show Toolbar"), this); connect(showMussaViewToolbarAction, SIGNAL(triggered()), @@ -139,18 +221,20 @@ void MussaWindow::setupActions() connect(toggleMotifsAction, SIGNAL(triggered()), this, SLOT(toggleMotifs())); toggleMotifsAction->setCheckable(true); - toggleMotifsAction->setIcon(QIcon("icons/motif_icon.png")); + toggleMotifsAction->setIcon(QIcon(":/icons/motif_icon.png")); toggleMotifsAction->setWhatsThis(tr("Toggle motif annotations on/off\n\n" "You can load motif annotations via " "'File->Load Motif List' menu option.")); //Save pixel map action saveBrowserPixmapAction = new QAction(tr("Save to image..."), this); - connect(saveBrowserPixmapAction, (SIGNAL(triggered())), - &browser, SLOT(promptSaveBrowserPixmap())); - saveBrowserPixmapAction->setIcon(QIcon("icons/image2.png")); + if (browser) { + connect(saveBrowserPixmapAction, (SIGNAL(triggered())), + browser, SLOT(promptSaveBrowserPixmap())); + saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png")); + } - viewMussaAlignmentAction = new QAction(tr("View mussa alignment"), this); + viewMussaAlignmentAction = new QAction(tr("View sequence alignment"), this); connect(viewMussaAlignmentAction, SIGNAL(triggered()), this, SLOT(viewMussaAlignment() )); viewMussaAlignmentAction->setWhatsThis(tr("Create a zoomed in window " @@ -158,21 +242,33 @@ void MussaWindow::setupActions() "defined paths")); whatsThisAction = QWhatsThis::createAction(this); - whatsThisAction->setIcon(QIcon("icons/help.png")); + whatsThisAction->setIcon(QIcon(":/icons/help.png")); + +} + +void MussaWindow::closeEvent(QCloseEvent *event) +{ + if(isClearingAnalysisSafe()) { + event->accept(); + } else { + event->ignore(); + } } void MussaWindow::setupMainMenu() { // we need to run setupActions first assert (closeAction != 0); + + QMenu *newMenu = menuBar()->addMenu(tr("&File")); - QMenu *newMenu; - newMenu = menuBar()->addMenu(tr("&File")); + newMenu->addAction(newMussaWindowAction); newMenu->addAction(createNewAnalysisAction); newMenu->addAction(loadMupaAction); newMenu->addAction(loadSavedAnalysisAction); - //newMenu->addAction(createSubAnalysisAction); + newMenu->addAction(saveAnalysisAction); + newMenu->addAction(saveAnalysisAsAction); newMenu->addSeparator(); newMenu->addAction(loadMotifListAction); newMenu->addAction(saveMotifListAction); @@ -181,113 +277,311 @@ void MussaWindow::setupMainMenu() newMenu->addSeparator(); newMenu->addAction(closeAction); - newMenu = menuBar()->addMenu(tr("&View")); + newMenu = menuBar()->addMenu(tr("&Edit")); newMenu->addAction(editMotifsAction); + if (browser) newMenu->addAction(browser->getCopySelectedSequenceAsStringAction()); + if (browser) newMenu->addAction(browser->getCopySelectedSequenceAsFastaAction()); + newMenu->addAction(createSubAnalysisAction); + if (browser) newMenu->addAction(browser->getEditSequencePropertiesAction()); + + newMenu = menuBar()->addMenu(tr("&View")); newMenu->addAction(viewMussaAlignmentAction); newMenu->addAction(showMussaViewToolbarAction); newMenu = menuBar()->addMenu(tr("&Help")); + newMenu->addAction(mussaManualAssistantAction); newMenu->addAction(whatsThisAction); newMenu->addSeparator(); newMenu->addAction(aboutAction); + + // add some extra features to the context menu + if (browser) { + QMenu *popupMenu = browser->getPopupMenu(); + if (popupMenu) { + popupMenu->addAction(viewMussaAlignmentAction); + popupMenu->addAction(createSubAnalysisAction); + } + } +} + +void MussaWindow::setupWidgets() +{ + setup_analysis_dialog = new MussaSetupDialog; + subanalysis_window.reset(new SubanalysisWindow(analysis)); + browser = new SequenceBrowserWidget(default_dir); + mussaViewTB = new QToolBar("Path Views", this); + zoom = new ZoomWidget(mussaViewTB); + threshold = new ThresholdWidget(mussaViewTB); +} + +void MussaWindow::setupAssistant() +{ +#if defined(QT_QTASSISTANT_FOUND) + /* + QStringList manualAssistantArgs; + manualAssistantArgs = QStringList(); + manualAssistantArgs << "-profile" << "./doc/manual/mussagl_manual.adp"; + manualAssistant = new QAssistantClient("assistant", this); + manualAssistant->setArguments(manualAssistantArgs); + connect(manualAssistant, SIGNAL(error(QString)), + this, SLOT(assistantError(QString))); + */ +#endif } void MussaWindow::about() { - QString msg("Welcome to Multiple Species Sequence Analysis\n" - "(c) 2005-2006 California Institute of Technology\n" - "Tristan De Buysscher, Diane Trout\n"); - msg += "\n\r"; - msg += "Path: "; - msg += QDir::currentPath(); - msg += "\n"; - msg += "OpenGL: "; - msg += (char *)glGetString(GL_VERSION); - msg += "\n"; - QMessageBox::about(this, tr("About mussa"), msg); + QString msg; + msg += "Welcome to Multiple Species Sequence Analysis\n"; + msg += "(c) 2005-2006 California Institute of Technology\n"; + msg += "Diane Trout, Tristan De Buysscher, Brandon King\n"; + msg += "Version: "; + msg += mussa_version; + msg += "\n"; + msg += "Qt: "; + msg += qVersion(); + msg += "\n"; + msg += "OpenGL: "; + msg += (char *)glGetString(GL_VERSION); + msg += "\n"; + QMessageBox::about(this, tr("About mussa"), msg); +} + +void MussaWindow::clear() +{ + if (motif_editor != 0) { + motif_editor->hide(); + delete motif_editor; + } + + aligned_windows.clear(); + browser->clear(); } void MussaWindow::createNewAnalysis() { - MussaSetupDialog *msd = new MussaSetupDialog(this); try { - if (msd->exec()) { - Mussa *m = 0; - m = msd->getMussa(); - setAnalysis(m); - } else { - std::cout << "New mussa exp. aborted!\n"; + // ideally we should open a new window if there's an analysis + // but this should work for the moment. + if (not isClearingAnalysisSafe()) return; + + if (setup_analysis_dialog->exec()) { + setAnalysis(setup_analysis_dialog->getMussa()); } } catch(mussa_error e) { QString msg(e.what()); QMessageBox::warning(this, tr("Create New Analysis"), msg); } - delete msd; } void MussaWindow::createSubAnalysis() { - NotImplementedBox(); + list result; + SequenceLocationModel& model = subanalysis_window->getModel(); + browser->copySelectedTracksAsSeqLocation(result); + for(list::iterator result_itor = result.begin(); + result_itor != result.end(); + ++result_itor) + { + model.push_back(*result_itor); + } + + if (not subanalysis_window->isVisible()) { + subanalysis_window->show(); + } +} + +void MussaWindow::saveAnalysis() +{ + // if we've got an analysis + if (analysis and not analysis->empty()) { + // if it doesn't have a name we need to pick one + if (analysis->get_analysis_path().empty()) { + saveAnalysisAs(); + } else { + // if we've got a name, has it changed any? + // this doesn't work when a sequence changes something (like its + // name) + //else if (analysis->is_dirty()) + try { + analysis->save(); + } catch (std::exception e) { + QMessageBox::critical(this, + tr("Mussa Save Error"), + tr(e.what()), + QMessageBox::Ok, 0, 0); + } + + } + } +} + +void MussaWindow::saveAnalysisAs() +{ + std::auto_ptr dialog(new QFileDialog(this)); + dialog->setAcceptMode(QFileDialog::AcceptSave); + dialog->setFileMode(QFileDialog::AnyFile); + dialog->setDirectory(*default_dir); + + QStringList fileNames; + if (not dialog->exec()) { + return; + } + fileNames = dialog->selectedFiles(); + + if (fileNames.size() != 1) { + return; + } + try { + fs::path save_path(fileNames[0].toStdString(), fs::native); + // do you want to overwrite? + if (fs::exists(save_path) and + QMessageBox::question( + this, + tr("Overwrite File? -- Mussa"), + tr("A file called %1 already exists" + "do you want to overwrite it?") + .arg(fileNames[0]), + tr("&Yes"), tr("&No"), + QString(), 0, 1) + ) { + return; + } + analysis->save(save_path); + fs::path normalized_path = (save_path / "..").normalize(); + default_dir->setPath(normalized_path.native_directory_string().c_str()); + } catch (std::exception e) { + QMessageBox::critical(this, + tr("Mussa Save Error"), + tr(e.what()), + QMessageBox::Ok, 0, 0); + } +} + +bool MussaWindow::isClearingAnalysisSafe() +{ + if (analysis and not analysis->empty() and analysis->is_dirty()) { + switch (QMessageBox::question( + this, + tr("Save Unsaved Changes -- Mussa"), + tr("There are unsaved changes,\ndo you want to save?"), + tr("&Yes"), tr("&No"), tr("&Cancel"), + 0, 2)) { + case 0: + // save + saveAnalysis(); + break; + case 1: + // don't save + break; + case 2: + // don't replace + return false; + break; + default: + // error + throw runtime_error("isClearingAnalysis QMesageBox failure"); + } + } + // if we're here we've been saved and can replace + return true; } void MussaWindow::editMotifs() { - if (motif_editor != 0) { - motif_editor->hide(); - delete motif_editor; + if (not motif_editor) { + motif_editor = new MotifEditor(analysis); + connect(motif_editor, SIGNAL(changedMotifs()), + this, SLOT(updateAnnotations())); } - motif_editor = new MotifEditor(analysis); - connect(motif_editor, SIGNAL(changedMotifs()), - this, SLOT(updateAnnotations())); motif_editor->show(); } void MussaWindow::loadMotifList() { - QString caption("Load a motif list"); + QString caption("Mussa Load Motifs"); QString filter("Motif list(*.txt *.mtl)"); QString path = QFileDialog::getOpenFileName(this, - caption, - QDir::currentPath(), - filter); + caption, + default_dir->absolutePath(), + filter); // user hit cancel? if (path.isNull()) return; // try to load safely try { - analysis->load_motifs(path.toStdString()); - } catch (runtime_error e) { + fs::path converted_path(path.toStdString(), fs::native); + analysis->load_motifs(converted_path); + default_dir->setPath(converted_path.branch_path().native_directory_string().c_str()); + emit changedMotifs(); + } catch (std::exception e) { QString msg("Unable to load "); msg += path; msg += "\n"; msg += e.what(); - QMessageBox::warning(this, "Load Motifs", msg); + QMessageBox::warning(this, caption, msg); } - assert (analysis != 0); } void MussaWindow::saveMotifList() { - NotImplementedBox(); -} + QString caption("Mussa Save Motifs"); + QString filter("Motif list(*.txt *.mtl)"); + QString path = QFileDialog::getSaveFileName(this, + caption, + default_dir->absolutePath(), + filter); + // user hit cancel? + if (path.isNull()) + return; + // try to load safely + try { + fs::path converted_path(path.toStdString(), fs::native); + if (fs::extension(converted_path).size() == 0) { + // no extension, so add one + fs::path base_path = converted_path.branch_path(); + fs::path filename(converted_path.leaf() + ".mtl", fs::native); + converted_path = base_path / filename; + } + analysis->save_motifs(converted_path); + default_dir->setPath(converted_path.branch_path().native_directory_string().c_str()); + } catch (std::exception e) { + QString msg("Unable to save "); + msg += path; + msg += "\n"; + msg += e.what(); + QMessageBox::warning(this, caption, msg); + }} void MussaWindow::loadMupa() { QString caption("Load a mussa parameter file"); QString filter("Mussa Parameters (*.mupa)"); - QString mupa_path = QFileDialog::getOpenFileName(this, - caption, - QDir::currentPath(), - filter); + QString mupa_path = QFileDialog::getOpenFileName( + this, + caption, + default_dir->absolutePath(), + filter + ); // user hit cancel? if (mupa_path.isNull()) return; // try to load safely try { - Mussa *m = new Mussa; - m->load_mupa_file(mupa_path.toStdString()); - m->analyze(0, 0, Mussa::TransitiveNway, 0.0); + // ideally we should open a new window if there's an analysis + // but this should work for the moment. + if (not isClearingAnalysisSafe()) return; + + MussaRef m = Mussa::init(); + fs::path converted_path(mupa_path.toStdString(), fs::native); + connect(m.get(), SIGNAL(progress(const QString&, int, int)), + this, SLOT(updateProgress(const QString&, int, int))); + m->load_mupa_file(converted_path); + m->analyze(); setAnalysis(m); + updateTitle(); + // grab the path ignoring the mupa file portion + default_dir->setPath(converted_path.branch_path().native_directory_string().c_str()); } catch (mussa_load_error e) { QString msg("Unable to load "); msg += mupa_path; @@ -301,18 +595,43 @@ void MussaWindow::loadMupa() void MussaWindow::loadSavedAnalysis() { QString caption("Load a previously run analysis"); - QString muway_dir = QFileDialog::getExistingDirectory(this, - caption, - QDir::currentPath()); + QString muway_dir = QFileDialog::getExistingDirectory( + this, + caption, + default_dir->absolutePath() + ); // user hit cancel? if (muway_dir.isNull()) return; // try to safely load try { - Mussa *m = new Mussa; - m->load(muway_dir.toStdString()); + // ideally we should open a new window if there's an analysis + // but this should work for the moment. + if (not isClearingAnalysisSafe()) return; + + MussaRef m = Mussa::init(); + fs::path converted_path(muway_dir.toStdString(), fs::native); + connect(m.get(), SIGNAL(progress(const QString&, int, int)), + this, SLOT(updateProgress(const QString&, int, int))); + m->load(converted_path); // only switch mussas if we loaded without error - setAnalysis(m); + if (analysis->empty()) { + // our current window is empty so load and replace. + setAnalysis(m); + updateTitle(); + default_dir->setPath(converted_path.branch_path().native_directory_string().c_str()); + } else { + MussaWindow *win = new MussaWindow(m); + updateTitle(); + win->default_dir->setPath(converted_path.branch_path().native_directory_string().c_str()); + win->show(); + } + } catch (boost::filesystem::filesystem_error e) { + QString msg("Unable to load "); + msg += muway_dir; + msg += "\n"; + msg += e.what(); + QMessageBox::warning(this, "Load Parameter", msg); } catch (mussa_load_error e) { QString msg("Unable to load "); msg += muway_dir; @@ -323,29 +642,105 @@ void MussaWindow::loadSavedAnalysis() assert (analysis != 0); } -void MussaWindow::setSoftThreshold(int threshold) +void MussaWindow::newMussaWindow() { - if (analysis->get_soft_thres() != threshold) { - analysis->set_soft_thres(threshold); + MussaWindow *win = new MussaWindow(Mussa::init()); + win->default_dir = default_dir; + win->show(); +} + +void MussaWindow::setSoftThreshold(int value) +{ + //std::cout << "Soft: " << analysis->get_soft_threshold() + // << " Value: " << value << "\n"; + if (analysis->get_soft_threshold() != value) { + threshold->setEnabled( false ); + //std::cout << "Updating!!!!\n"; + analysis->set_soft_threshold(value); analysis->nway(); updateLinks(); update(); + threshold->setEnabled( true ); } + //else + //{ + // std::cout << "NOT Updating!!!!\n"; + //} } void MussaWindow::showMussaToolbar() { - if (mussaViewTB.isVisible()) - mussaViewTB.hide(); + if (mussaViewTB->isVisible()) + mussaViewTB->hide(); else - mussaViewTB.show(); + mussaViewTB->show(); +} + +void MussaWindow::showBasePairsCopied(size_t bp_copied) +{ + QString msg("Copied "); + QString num; + num.setNum(bp_copied); + msg += num + " base pairs"; + statusBar()->showMessage(msg, 5000); } + void MussaWindow::toggleMotifs() { NotImplementedBox(); } +void MussaWindow::showManual() +{ +#if defined(QT_QTASSISTANT_FOUND) + + // Only define the process once. + if (!assistantProcess) + assistantProcess = new QProcess(this); + + // No need to fire up the process again if it is already running. + if (assistantProcess->state() == QProcess::Running) + return; + + QString app = QLibraryInfo::location(QLibraryInfo::BinariesPath) + + QLatin1String("/assistant"); + + assistantProcess->start(app, QStringList() << QLatin1String("-enableRemoteControl") + << QLatin1String("-collectionFile") << QLatin1String("mussagl_manual.qhc")); + if (!assistantProcess->waitForStarted()) { + QMessageBox::critical(this, tr("Remote Control"), + tr("Could not start Qt Assistant from %1.").arg(app)); + return; + } + + // show index page + QTextStream str(assistantProcess); + str << QLatin1String("SetSource qthelp://edu.caltech.woldlab.mussagl.1_0_0/doc/mussagl_manual.html;") + << QLatin1String("expandToc 0") + << QLatin1Char('\0') << endl; + +#else + QUrl manual_url("http://woldlab.caltech.edu/~king/mussagl_manual/"); + if (not QDesktopServices::openUrl(manual_url)) { + QMessageBox::warning(this, + tr("Mussa Help Error"), + tr("Unable to launch webbrowser"), + QMessageBox::Ok, + QMessageBox::NoButton, + QMessageBox::NoButton); + } +#endif //QT_QTASSISTANT_FOUND +} + +void MussaWindow::assistantError(QString message) +{ + //std::cout << "QAssistantError: " << message.toStdString() << "\n"; + QMessageBox::warning ( this, "Warning: Mussagl Manual", message, + QMessageBox::Ok, + QMessageBox::NoButton); +} + void MussaWindow::NotImplementedBox() { QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("Not implemented yet")); @@ -353,40 +748,38 @@ void MussaWindow::NotImplementedBox() void MussaWindow::viewMussaAlignment() { - const set& selected_paths = browser.selectedPaths(); + const set& selected_paths = browser->selectedPaths(); if (selected_paths.size() == 0 ) { QMessageBox::warning(this, QObject::tr("mussa"), QObject::tr("you should probably select some paths " "first")); } else { - MussaAlignedWindow *ma_win = new MussaAlignedWindow(*analysis, - selected_paths); - connect(this, SIGNAL(changedAnnotations()), - ma_win, SLOT(update())); + MussaAlignedWindowRef ma_win( + new MussaAlignedWindow(analysis, default_dir, selected_paths, subanalysis_window) + ); + aligned_windows.push_back(ma_win); - ma_win->show(); + connect(this, SIGNAL(changedAnnotations()), + aligned_windows.back().get(), SLOT(update())); + aligned_windows.back()->show(); } } void MussaWindow::updateAnalysis() { - cout << "analysis updated" << endl; - for (list::iterator maw_i = aligned_windows.begin(); - maw_i != aligned_windows.end(); - ++maw_i) - { - (*maw_i)->hide(); - delete *maw_i; - } - - browser.clear(); - const vector& seqs = analysis->sequences(); - browser.setSequences(seqs, analysis->colorMapper()); + const Mussa::vector_sequence_type& seqs = analysis->sequences(); + browser->setSequences(seqs, analysis->colorMapper()); + assert(browser->sequences().size() == analysis->size()); + + // setRange eventually emits something that causes updateLinks to be called + // but it's possible for us to not have had a chance to set out sequences + // yet. + threshold->setRange(analysis->get_threshold(),analysis->get_window()); + threshold->setBasepairThreshold(analysis->get_soft_threshold()); updateLinks(); - browser.zoomOut(); - cout << "browser zoom " << browser.zoom() << endl; - zoomBox.setValue(browser.zoom()); + browser->zoomOut(); + zoom->setValue(browser->zoom()); } void MussaWindow::updateAnnotations() @@ -394,16 +787,21 @@ void MussaWindow::updateAnnotations() // motifs were changed in the sequences by // Mussa::update_sequences_motifs emit changedAnnotations(); - browser.update(); + browser->update(); } void MussaWindow::updateLinks() { - browser.clear_links(); + if(browser->sequences().size() == 0) { + // we don't have any sequences load so we have no business setting links + return; + } + + browser->clear_links(); bool reversed = false; const NwayPaths& nway = analysis->paths(); - typedef list conserved_paths; + typedef list conserved_paths; typedef conserved_paths::const_iterator const_conserved_paths_itor; for(const_conserved_paths_itor path_itor = nway.refined_pathz.begin(); path_itor != nway.refined_pathz.end(); @@ -431,13 +829,51 @@ void MussaWindow::updateLinks() reversed = false; } else { reversed = true; - x = -x; // make positive + // make positive + x = -x; } normalized_path.push_back(x); rc_flags.push_back(reversed); } - browser.link(normalized_path, rc_flags, path_itor->window_size); + browser->link(normalized_path, rc_flags, path_itor->window_size); + } + browser->update(); +} + +void +MussaWindow::updateProgress(const QString& description, int current, int max) +{ + // if we're done + if (current == max) { + if (progress_dialog != 0) { + progress_dialog->hide(); + delete progress_dialog; + progress_dialog = 0; + } + } else { + // if we're starting, create the dialog + if (progress_dialog == 0) { + QString cancel("Cancel"); + progress_dialog = new QProgressDialog(description, cancel, current, max, this); + progress_dialog->show(); + } else { + // just update the dialog + progress_dialog->setValue(current); + } } - browser.update(); + qApp->processEvents(); } +void MussaWindow::updateAnalysisModified(bool is_modified) +{ + setWindowModified(is_modified); +} + +void MussaWindow::updateTitle() +{ + if (analysis) { + QString title(analysis->get_title().c_str()); + title += "[*]"; + setWindowTitle(title); + } +}